Hibernate(5)—— 联合主键 、一对一关联关系映射(xml和注解) 和 领域驱动设计

俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及的知识点总结如下:

  • One to One 映射关系

    • 一对一单向外键(XML/Annotation)
    • 一对一双向外键关联(XML/Annotation)
    • 联合主键
    • 一对一单向外键联合主键(Xml/Annotation)
    • 一对一组件关联(XML/Annotation)
    • 理解组件
  • 领域驱动设计——自动生成数据库脚本
  • 一对一关系的小结
  • 一些出错问题的总结


 

  自动生成数据库脚本

  一般在项目开发过程中,我们的习惯是先建好数据库和表,然后在进行开发,而hibernate作为一款ORM架构模式的实现框架,我们要好好利用,可以利用hibernate反向工程生成*.hbm.xml文件跟POJO类,个人认为由于目前所使用的数据库大部分还都是关系数据库,而hibernate把对数据库的操作都对象化了,更应当从对象出发,生成数据库里面相关表,这样更加符合人认知事物的习惯,hibernate3就已经提供了自带的工具hbm2ddl,能够通过对象建立数据库脚本。

  要生成数据库表,得先建好实体类,然后建立对应的配置文件(or 注解),最后和主配置文件hibernate.cfg.xml关联即可。作用的代码如下:

 View Code

  这就是领域驱动设计/开发(DDD,Domain Driven Design/Development)。

  系统的设计应该基于对象模型,考虑对象的设计和逻辑,然后按照对象模型建立数据库关系模型,这才是现在面向对象开发的步骤,不是说先设计数据库,然后再设计对象。当在设计时,我们的领域模型需要改变,只需修改Hibernate的结构和应用程序,不需要修改数据库架构,只要利用SchemaExport工具重新生成数据库架构就ok。

 

  一对一的单向外键关系

  比如一个人对应一张身份证,反过来一张身份证必须对应一个人,不存在歧义。一个学生类,一个身份证类,学生唯一对应一张身份证,反过来,身份证唯一表示一个学生。这就是一对一的关系,其中学生能持有身份证,故学生表里的身份证的id就是学生表的外键,学生id是学生表的主键。这也叫一对一单向外键关系。

  注解方式:实体类和配置如下:

 View Code

下面是学生类:

 View Code

最后反向生成数据库表。注意:一对一的关系,必须先保存外键的对象,再保存主键的对象,完整的CRUD的代码如下

 View Code

Hibernate: insert into IdCard (province, pid) values (?, ?)
Hibernate: insert into Students (pid, sname) values (?, ?)

 

  XML配置方式:注意主控方的配置文件里需要加上<many-to-one name="cardId" column="pid" unique="true"/>,主控方就是持有唯一外键的那个表,name=外键id,保持唯一,因为一对一,必须设置为unique为true!

身份证的配置:

<hibernate-mapping>
  <class name="net.nw.vo.fk.IdCard" table="idcard">
    <id name="pid" column="pid" type="string">
      <generator class="assigned"/>
    </id>

    <property name="province" column="province" type="string"/>
  </class>
</hibernate-mapping>

学生的配置:作为主控方,需要加上一对一的关系映射属性,使用的是many-to-one标签

<hibernate-mapping>
  <class name="net.nw.vo.fk.Students" table="students">
    <id name="sid" column="sid" type="int">
      <generator class="native"/>
    </id>

    <property name="sname" column="sname" type="string"/>
    <many-to-one name="cardId" column="pid" unique="true"/>
  </class>
</hibernate-mapping>

还有主配置文件的关联,下面是测试代码:

    public void testSave() {
        Session session = sessionFactory.getCurrentSession();
        Transaction tx = session.beginTransaction();

        try    {
            IdCard c = new IdCard();
            c.setPid("1111111111");
            c.setProvince("dadadada");
            Students s = new Students();
            s.setSname("daaaaaaa");
            s.setCardId(c);

            //先保存身份证
            session.save(c);
            //再保存学生
            session.save(s);

            tx.commit();
        } catch(Exception ex) {
            ex.printStackTrace();
            tx.rollback();
        }
    }

成功插入了数据

 

  一对一双向外键关联

  很简单,可以理解为双方都持有对方的引用,也就是你中有我,我中有你的一对一关联关系,比如学生表包含身份证id,同时身份证表也包含学生id,同时互为对方的外键。

  注解方式:实体类和配置如下:

  两个实体类其中之一必须使用注解:@OneToOne(mappedBy=“实体类的引用”) ,属性mappedBy是必须的,目的是设置将控制权交给另外一方(实体类)。双向的关联必须设置mappedBy属性。也就是说双向关联只能交给一方去控制,不可能在双方都设置外键保存关联关系,否则都无法保存。因为学生表包含身份证id,同时身份证表也包含学生id,互为对方的外键。如果互为主控方,那么先保存谁呢?如果要保存学生,则应该先保存身份证,而身份证也是主控方,要保存身份证必须先保存学生……死循环发生。

 1 import javax.persistence.*;
 2
 3 //学生实体类
 4 @Entity
 5 public class Students {
 6     private int sid;  //编号
 7
 8     private String sname; //姓名
 9
10     private IdCard cardId; // 学生持有身份证的引用,因为学生表关联了身份证表
11
12 //    cascade属性需要设置为all,表明删除student时同时删除对应的身份证
13     @OneToOne(cascade = CascadeType.ALL) // 本例子里学生作为主控方,则需要在身份证类那儿声明mappedBy属性
14     @JoinColumn(name="pid",unique=true) // 学生表的外键是身份证id:pid,唯一的
15     public IdCard getCardId() {
16         return cardId;
17     }
18
19     public void setCardId(IdCard cardId) {
20         this.cardId = cardId;
21     }
22
23     @Id
24     @GeneratedValue
25     // 默认 等价于 @GeneratedValue(strategy=GenerationType.AUTO)
26     public int getSid() {
27         return sid;
28     }
29
30     public void setSid(int sid) {
31         this.sid = sid;
32     }
33
34     public String getSname() {
35         return sname;
36     }
37
38     public void setSname(String sname) {
39         this.sname = sname;
40     }
41 }

  本例子里学生表(实体类)作为主控方,那么本类不需要设置@OneToOne(mappedBy=“”),而是需要在另一方——身份证类里声明@OneToOne(mappedBy=“”)属性

 1 import org.hibernate.annotations.GenericGenerator;
 2 import javax.persistence.*;
 3
 4 //身份证类
 5 @Entity
 6 public class IdCard {
 7     private String pid;//身份证号码
 8
 9     private String province;//省份
10
11     private Students stu;//反过来身份证表也关联学生表,对应程序也就是身份证类包含学生对象的引用
12
13     // 身份证把控制权交给学生控制,mappedBy值是主控方持有的引用(外键)
14     // 记住:只要双向关联,必须双方有一方要指定mappedBy属性,不能互相指定,目的是避免死循环发生
15     @OneToOne(mappedBy="cardId")
16     public Students getStu() {
17         return stu;
18     }
19
20     public void setStu(Students stu) {
21         this.stu = stu;
22     }
23
24     @Id
25     @GeneratedValue(generator="pid")
26     @GenericGenerator(name="pid",strategy="assigned")
27     public String getPid() {
28         return pid;
29     }
30
31     public void setPid(String pid) {
32         this.pid = pid;
33     }
34
35     public String getProvince() {
36         return province;
37     }
38
39     public void setProvince(String province) {
40         this.province = province;
41     }
42 }

  测试生成数据库脚本:

alter table Students drop foreign key FK73AC29B8917F52BE
drop table if exists IdCard
drop table if exists Students
create table IdCard (pid varchar(255) not null, province varchar(255), primary key (pid))
create table Students (sid integer not null auto_increment, sname varchar(255), pid varchar(255) unique, primary key (sid))
alter table Students add index FK73AC29B8917F52BE (pid), add constraint FK73AC29B8917F52BE foreign key (pid) references IdCard (pid)

  我发现其实和刚刚一对一单向外键关联的表结构一样,好像没什么区别啊?其实关键问题是所谓的一对一双向关联,就是实体类里虽然彼此包含了对方的引用,但是在实际设计数据库时的关键问题是——把控制权交给谁?上例把IdCard的stu交给了学生类去控制,如果反过来,学生类stu的身份证pid交给身份证去控制,看代码:

 1 import org.hibernate.annotations.GenericGenerator;
 2 import javax.persistence.*;
 3
 4 //身份证类
 5 @Entity
 6 public class IdCard {
 7     private String pid;//身份证号码
 8
 9     private String province;//省份
10
11     private Students stu;//反过来身份证表也关联学生表,对应程序也就是身份证类包含学生对象的引用
12
13     @OneToOne(cascade=CascadeType.ALL) // 现在身份证是主控方
14     @JoinColumn(name="sid",unique=true)
15     public Students getStu() {
16         return stu;
17     }
18
19     public void setStu(Students stu) {
20         this.stu = stu;
21     }
22
23     @Id
24     @GeneratedValue(generator="pid")
25     @GenericGenerator(name="pid",strategy="assigned")
26     public String getPid() {
27         return pid;
28     }
29
30     public void setPid(String pid) {
31         this.pid = pid;
32     }
33
34     public String getProvince() {
35         return province;
36     }
37
38     public void setProvince(String province) {
39         this.province = province;
40     }
41 }

  主控方交给身份证

 1 import javax.persistence.*;
 2
 3 //学生实体类
 4 @Entity
 5 public class Students {
 6     private int sid;  //编号
 7
 8     private String sname; //姓名
 9
10     private IdCard cardId; // 学生持有身份证,学生表关联了身份证表
11
12     @OneToOne(mappedBy="stu") // 学生持有的身份证把控制权交给身份证控制
13     public IdCard getCardId() {
14         return cardId;
15     }
16
17     public void setCardId(IdCard cardId) {
18         this.cardId = cardId;
19     }
20
21     @Id
22     @GeneratedValue
23     // 默认 等价于 @GeneratedValue(strategy=GenerationType.AUTO)
24     public int getSid() {
25         return sid;
26     }
27
28     public void setSid(int sid) {
29         this.sid = sid;
30     }
31
32     public String getSname() {
33         return sname;
34     }
35
36     public void setSname(String sname) {
37         this.sname = sname;
38     }
39 }

  测试数据库脚本,发现和刚刚不一样了,身份证里有外键sid,而学生表没有了之前的外键pid,说明身份证去控制学生了。

create table IdCard (pid varchar(255) not null, province varchar(255), sid integer unique, primary key (pid))

create table Students (sid integer not null auto_increment, sname varchar(255), primary key (sid))

  注意:千万别让双方都持有对方的引用作为外键,这样是没有意义的。虽然语法不会出错,执行起来肯定会出死循环的问题。继续测试,现在是身份证控制学生,那么先保存学生(学生id是外键),在保存身份证:

 1 import org.hibernate.Session;
 2 import org.hibernate.SessionFactory;
 3 import org.hibernate.Transaction;
 4 import org.hibernate.cfg.AnnotationConfiguration;
 5 import org.hibernate.cfg.Configuration;
 6 import org.hibernate.tool.hbm2ddl.SchemaExport;
 7 import org.junit.After;
 8 import org.junit.Before;
 9 import org.junit.Ignore;
10 import org.junit.Test;
11
12 public class TestStudentsByAnno {
13     private static SessionFactory sessionFactory;
14
15     @Before
16     public void setUp() throws Exception {
17         System.out.println("setUp()...");
18         sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
19     }
20
21     @After
22     public void tearDown() throws Exception {
23         System.out.println("tearDown()...");
24         sessionFactory.close();
25     }
26
27     @Test
28
29     public void testSave() {
30         Session session = sessionFactory.getCurrentSession();
31         Transaction tx = session.beginTransaction();
32
33         try    {
34             Students s = new Students();
35             s.setSname("xiaoli");
36             IdCard c = new IdCard();
37             c.setPid("12345656788");
38             c.setProvince("hebei");
39             c.setStu(s);
40             //先保存学生
41             session.save(s);
42             //再保存身份证
43             session.save(c);
44
45             tx.commit();
46         } catch(Exception ex)    {
47             ex.printStackTrace();
48             tx.rollback();
49         }
50     }
51
52     @Test
53     @Ignore
54     public void testSchemaExport() {
55         SchemaExport se = new SchemaExport(new Configuration().configure());
56         se.create(true, true);
57     }
58 }

结果:

 

  XML配置方式:实体类和配置如下:

  大同小异,理解了注解的方式,xml就是书写的注意,主控方还是和之前一对一单向管理的写法一样:<many-to-one name="cardId" column="pid" unique="true"/>,被控方需要注意下,要写成<one-to-one name="stu" property-ref="cardId"/>,下面实现一个学生为主控方的例子:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <class name="net.nw.vo.bfk.Students" table="students">
    <id name="sid" column="sid" type="int">
      <generator class="native"/>
    </id>

    <property name="sname" column="sname" type="string"/>
    <!-- 主控方,外键是身份证pid -->
    <many-to-one name="cardId" column="pid" unique="true"/>
  </class>
</hibernate-mapping>

-----------------------------------------------------------------------------
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <class name="net.nw.vo.bfk.IdCard" table="idcard">
    <id name="pid" column="pid" type="string">
      <generator class="assigned"/>
    </id>

    <property name="province" column="province" type="string"/>
    <!-- 身份证是被控的,也需要配置,使用one-to-one属性
     property-ref意思是把控制交给学生类,让学生使用cardId这个身份证的引用去控制
     -->
    <one-to-one name="stu" property-ref="cardId"/>
  </class>
</hibernate-mapping>

  实体类不变,还是互相持有对方的引用,然后测试也是注意,先保存外键的实体类身份证,最后保存学生类:

            IdCard c = new IdCard();
            c.setPid("999999999");
            c.setProvince("beijing");
            Students s = new Students();
            s.setSname("wangwu");
            s.setCardId(c);

            //先保存身份证
            session.save(c);
            //再保存学生
            session.save(s);

            tx.commit();

 

  什么是联合主键?

  联合主键就是用多个字段组成的主键。用这个主键包含的字段作为主键,这个组合在数据表中是唯一,且加了主键索引。比如,订单表里有很多字段,一般情况只要有个订单号做主键就可以,但是现在可能会有补充订单,使用相同的订单号,那么这时单独使用订单号就不可以,因为会有重复。那么可以再使用个订单序列号作为区别。把订单号和订单序列号设成联合主键。即使订单号相同,订单序列号不同也是可以唯一的。

 

  一对一单向外键联合主键关联

  之前的主键都是一个字段,比如身份证的主键pid,现在在之前的身份证id上加一个血型字段作为联合主键,那么需要新建一个联合主键类,代码如下:

  注解方式:实体类和配置如下:

  先创建联合主键类,须实现serializable接口,进行序列化,还必须重写hashCode()和equals()方法。联合主键类的注解是@Embeddable,使能嵌入的意思,而实体类的主键使用@EmbeddedId标识,代码如下:

import javax.persistence.Embeddable;
import java.io.Serializable;

//联合主键类的注解是 Xxxxable 类型,嵌入的意思
@Embeddable
public class IdCardPK implements Serializable{
    /**
     * 必须实现序列化接口
     */
    private static final long serialVersionUID = 1L;

    private String pid;//身份证号码,主键字段

    private String bloodType;//新加的字段:血型,也作为主键字段

    public String getPid() {
        return pid;
    }

    public void setPid(String pid) {
        this.pid = pid;
    }

    public String getBloodType() {
        return bloodType;
    }

    public void setBloodType(String bloodType) {
        this.bloodType = bloodType;
    }

    /**
     * 必须重写equals和hashCode
     *
     * @param obj Object
     * @return boolean
     */
    @Override
    public boolean equals(Object obj) {
        return super.equals(obj);
    }

    @Override
    public int hashCode() {
        return super.hashCode();
    }
}

  再创建对应的实体类:

 1 import javax.persistence.EmbeddedId;
 2 import javax.persistence.Entity;
 3 import javax.persistence.OneToOne;
 4
 5 //身份证实体类
 6 @Entity
 7 public class IdCard {
 8     private IdCardPK pk;// 身份证的主键是联合主键 IdCardPK pk
 9
10     private String province;// 省份
11
12     private Students stu; // 身份证持有学生类的引用
13
14 //    把控制权交给学生
15     @OneToOne(mappedBy="cardId")//只要是双向关联,就一定要指定mappedBy
16     public Students getStu() {
17         return stu;
18     }
19
20     public void setStu(Students stu) {
21         this.stu = stu;
22     }
23
24 //    联合主键的主键形式是Xxxxid类型,具体的主键设置在联合主键类里进行
25     @EmbeddedId
26     public IdCardPK getPk() {
27         return pk;
28     }
29
30     public void setPk(IdCardPK pk) {
31         this.pk = pk;
32     }
33
34     public String getProvince() {
35         return province;
36     }
37
38     public void setProvince(String province) {
39         this.province = province;
40     }
41 }
42
43 import javax.persistence.CascadeType;
44 import javax.persistence.Entity;
45 import javax.persistence.GeneratedValue;
46 import javax.persistence.Id;
47 import javax.persistence.JoinColumn;
48 import javax.persistence.JoinColumns;
49 import javax.persistence.OneToOne;
50
51 //学生实体类
52 @Entity
53 public class Students {
54     private int sid;  //编号
55
56     private String sname; //姓名
57
58     private IdCard cardId; // 学生的外键,学生控制身份证
59
60 //    主控类的外键设置比较复杂点,因为有多个主键字段了
61     @OneToOne(cascade=CascadeType.ALL)
62     @JoinColumns(
63             {
64 //                    referencedColumnName设置对应数据库的字段名字,name是类的字段名字
65                     @JoinColumn(name="pid",referencedColumnName="pid"),
66                     @JoinColumn(name="bloodType",referencedColumnName="bloodtype")
67             }
68     )
69     public IdCard getCardId() {
70         return cardId;
71     }
72
73     public void setCardId(IdCard cardId) {
74         this.cardId = cardId;
75     }
76
77     @Id
78     @GeneratedValue
79     public int getSid() {
80         return sid;
81     }
82
83     public void setSid(int sid) {
84         this.sid = sid;
85     }
86
87     public String getSname() {
88         return sname;
89     }
90
91     public void setSname(String sname) {
92         this.sname = sname;
93     }
94 }

  注意千万别忘了配置hibernate主文件:

<mapping class="net.nw.vo.ufk.Students" />
<mapping class="net.nw.vo.ufk.IdCard" />
<mapping class="net.nw.vo.ufk.IdCardPK" />

  生成的数据库脚本:

create table IdCard (bloodType varchar(255) not null, pid varchar(255) not null, province varchar(255), primary key (bloodType, pid))
create table Students (sid integer not null auto_increment, sname varchar(255), bloodType varchar(255), pid varchar(255), primary key (sid))

  同样保存数据也是先保存受控类,在保存主控类。

 

  XML配置方式:实体类和配置如下:

   理解了上述主键的意思,xml也就很简单了:

  被控方:

<composite-id name="pk" class="联合主键的类">

  <key-property name="pid" column="pid" type="string"/>

  <key-property name="bloodType" type="string"/>

  <generator class="assigned"></generator>

</composite-id>

  主控方:

<many-to-one name="cardId">

   <column name="pid" unique="true"/>

   <column name="bloodid"/>

</many-to-one>

注意,联合主键类不需要单独的映射文件进行配置!代码如下:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <class name="net.nw.vo.ufk.IdCard" table="idcard">
      <!-- 联合主键的配置 -->
   <composite-id name="pk" class="net.nw.vo.ufk.IdCardPK">
      <key-property name="pid" column="pid" type="string"/>
      <key-property name="bloodType" type="string"/>
      <generator class="assigned"/>
   </composite-id>

   <property name="province" column="province" type="string"/>
  </class>
</hibernate-mapping>
------------------------------------------------------------------

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <!-- 主控方-->
  <class name="net.nw.vo.ufk.Students" table="students">
    <id name="sid" column="sid" type="int">
      <generator class="native"/>
    </id>
    <property name="sname" column="sname" type="string"/>

    <!-- 主控方的外键 -->
    <many-to-one name="cardId">
      <!-- 该外键的表又包含联合主键 -->
      <column name="pid" unique="true"/>
      <column name="bloodid"/>
    </many-to-one>
  </class>
</hibernate-mapping>

 

  一对一组件关联映射

  组件类就是一个POJO类,什么主键或者映射配置都不需要!因为实际上就是只有一张表,是最简单的,先不说理论,看例子:

  注解方式:实体类和配置如下:

  IdCard里什么主键都不写,当然配置也不需要,就是一个POJO类

//身份证类,就是一个POJO类
public class IdCard {
    private String pid;//身份证号码

    private String province;//省份

    public String getPid() {
        return pid;
    }

    public void setPid(String pid) {
        this.pid = pid;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }
}

  只有被组件注入的实体类需要配置,它的主键注解为@Embedded

import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

//学生实体类
@Entity
public class Students {
    private int sid;  //编号

    private String sname; //姓名

    private IdCard cardId;

    @Id
    @GeneratedValue
    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    @Embedded
    public IdCard getCardId() {
        return cardId;
    }

    public void setCardId(IdCard cardId) {
        this.cardId = cardId;
    }
}

  执行数据库脚本:

drop table if exists Students
create table Students (sid integer not null auto_increment, pid varchar(255), province varchar(255), sname varchar(255), primary key (sid))

  执行插入测试:

            IdCard c = new IdCard();
            c.setPid("1234567");
            c.setProvince("hebei");

            Students s = new Students();
            s.setSname("a");
            s.setCardId(c);

            session.save(s);

            tx.commit();

  

  开始扯理论:如何理解组件?
组件是某个实体类的逻辑组成部分,这个组件与实体的本质区别在于组件没有oid(参考前面的例子,它没有任何配置),故可以理解为能把组件当做值对象。例子中把省份和身份证id单独提取抽象出来作为了一个组件类,这个类就叫做值对象,也就是所说的组件。

  采用组件映射的优点

  实现了对象细粒度的划分,层次更加分明,复用率高。上面的student类,分为了基本信息sid、sname,还将身份证分离了出来,此外,还可以将爱好,家庭成员等信息再作为一类分离出来,进行细粒度的划分。分离出来的组件,也可以作为其他对象(例如teacher、employer等)的组件,这样就为复用提供了方便。

 

  XML配置方式:实体类和配置如下:

  映射关系文件: 

<component name="cardId" class="net.nw.vo.component.IdCard">

  <property name="pid" column="pid" type="string"></property>

  <property name="province" type="string"></property>

</component>

映射文件中,通过<component>标签,将组件类的属性映射到学生表中,这样,student表中的字段就会是Student类中的sid、sname以及组件类中的pid和provincel。而在数据库中,身份证类不用单独创建一张表。代码如下:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <class name="net.nw.vo.bfk.Students" table="students">
    <id name="sid" column="sid" type="int">
      <generator class="native"/>
    </id>

    <property name="sname" column="sname" type="string"/>

    <component name="cardId" class="net.nw.vo.component.IdCard">
     <property name="pid" column="pid" type="string"></property>
     <property name="province" type="string"></property>
    </component>
  </class>
</hibernate-mapping>

 

  一对一映射关系小结:

  再看一个例子,比如web开发中最最常见的用户表,里面有联系方式和姓名:

  实体细粒度模型

 View Code

  粗粒度的数据库模型

  实体一对一关联:假如数据库中已经存在contact、name表,并且Contact、Name设计成实体类,则Tusers 类与Contact,Name类之间成为一对一关联关系。否则就是组件映射关系。

 

  问题小结:

  期间使用注解的时候出现了一些瑕疵:

  1. 忘记导入dom4j包
  2. 忘记导入hibernate注解需要用到的ejb3-persistence.jar(其余的两个是hibernate-annotations.jar,hibernate-commons-annotations.jar包)。
  3. org.hibernate.MappingException: Unknown entity: xxxx异常:
    1. 可能是因为使用注解的时候没有导入正确的包。Entity包是javax.persistence.Entity;而不是hibernate包下的annotation。
    2. 使用注解时没有在*.cfg.xml下配置<mapping class=""/>,从而造成org.hibernate.MappingException: Unknown entity异常的
  4. java.lang.NoClassDefFoundError: javax/persistence/Cacheable异常: javax.persistence.Cacheable 是 JPA 2.0 规范中的东西,需要导入jar包:hibernate-jpa-2.0-api-1.0.1.Final.jar
  5. 关于java.lang.NoSuchMethodError: javax.persistence.OneToOne.orphanRemoval()Z异常:JUnit报Caused by: java.lang.NoSuchMethodError: javax.persistence.OneToOne.orphanRemoval()Z,原因是ejb3-persistence.jar和hibernate-jpa-2.0-api-1.0.1.Final.jar有冲突,删掉ejb3-persistence.jar即可。
  6. 千万注意hibernate主配置文件里必须引入mapping——实体关系映射
  7. 注意区分注解和配置文件的实体映射关系在主配置文件的写法。
  8. 联合主键类必须实现序列化接口和重写equals和hashCode方法。
  9. Specified key was too long; max key length is 1024 bytes异常,这个是MySQL5.2.0以及之前的bug,升级数据库或者减少联合主键的字段长度就能解决,当MySQL数据库的字符集为latin1时,是没有问题的,如果用UTF8字符集,就会报这个错误,因为在设置联合主键时,如果采用UTF-8编码,那么只能存储1024/3=300多个单位长度,所以长度不能超过300.

 

辛苦的劳动,转载请注明出处,谢谢……http://www.cnblogs.com/kubixuesheng/p/5281531.html

时间: 2024-08-29 07:49:32

Hibernate(5)—— 联合主键 、一对一关联关系映射(xml和注解) 和 领域驱动设计的相关文章

【HIBERNATE框架开发之四】HIBERNATE-ANNOTATION常用的注解归总&amp;&amp;ID的生成策略&amp;&amp;联合主键

本站文章均为 李华明Himi 原创,转载务必在明显处注明:  转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/hibernate/811.html 这篇主要讲解Hibernate中Annotation的常用注解和ID的生成策略以及联合主键三块:     首先介绍些常用的Annotation注解: 1.  当表名与类名不一致: @Table(name="数据库表名")        (javax.persistence)      如果类名与

[Hibernate开发之路](5)联合主键

现在大家都不推荐使用联合主键,关键是因为其需要自己手工维护,比较麻烦.但是一个项目可能因为历史遗留原因,你不得不面对联合主键.  Hibernate联合主键问题解决如下: 可以使用一个组件作为一个实体类的标识符.你的组件类必须满足以下要求:  (1)它必须实现 java.io.Serializable 接口  (2)它必须重新实现 equals() 和 hashCode() 方法,始终和组合关键字在数据库中的概念保持一致  注意:在 Hibernate3 中,第二个要求并非是 Hibernate

用Hibernate配置有联合主键时,出现问题???

问题描述 我在配置图书和读者关系的时候,考虑到双方是多对多的关系,所以另起了一个中间表"borrow",在配置联合主键上面出错了,可能是配置双方一对多的关系的时候有问题:org.hibernate.MappingException:AnassociationfromthetableBOOKreferstoanunmappedclass:com.library.po.Borrow:可不可以帮我弄弄,我这个不太熟悉??谢谢大家了!! 解决方案 解决方案二:亲贴代码出来,我是hibernat

Hibernate为什么不推荐使用联合主键

问题描述 Hibernate为什么不推荐使用联合主键 Hibernate为什么不推荐使用联合主键,能说明一下原因吗?谢谢! 解决方案 读取的性能比较差,O/R Mapping都是以主键的方式来识别纪录的,单一主键已经能够满足应用,而且速度快,所以会值得推荐 解决方案二: 如果当你使用无意义的逻辑主键的时候,主键的维护完全是由Hibernate自动进行的,你无须关注主键的维护,自然就避免了很多问题的产生:而如果你选择自己手工维护主键(联合主键就必须手工维护),所有的这些维护主键的重任都必须由你来负

(论坛答疑点滴)联合主键的情况怎么在DataGrid中利用DataKeys定位记录?

datagrid   比如表中三个字段 key1 int,key2 int,item varchar(50) 前面2个字段联合主键 前台代码: <asp:DataGrid id="DataGrid1" runat="server" AutoGenerateColumns="False" DataKeyField="DoubleKey">                 <Columns>        

mysql auto_increment 与 联合主键冲突问题

mysql 5.5之前,auto_increment字段必须为主键,有的时候,这种自增字段,并没有多大的实际意义,而我们需要多个字段组成主键. 例如: 用户只能对购买的商品,进行一次评价,评价的自增ID,没什么用处,用户的ID和商品ID组成的联合主键,意义就大的多了.如果想保留auto_increment字段,就要使用高版本的mysql了. mysql> ALTER TABLE `order` DROP PRIMARY KEY ,ADD PRIMARY KEY ( `user_id` , `or

mysql-MySQL不支持Hibernate的sequence主键生成策略吗?

问题描述 MySQL不支持Hibernate的sequence主键生成策略吗? 网上都说MySQL不支持Hibernate的sequence主键生成策略.为什么我的实体类使用JAP的@GeneralValue(好像是这个,没有指定生成策略)的时候,数据库会产生hibernate_sequence表?? 解决方案 hibernate主键生成策略值sequenceHibernate主键生成策略Hibernate主键生成策略

oracle联合主键问题,2个主键确定数据

问题描述 oracle联合主键问题,2个主键确定数据 #机构信息表T_GX_JGXX = Table('T_GX_JGXX'con.metadataColumn('YXJGDM' String(30) nullable=Falsedoc='银行机构代码')Column('NBJGH' String(30) nullable=Falseprimary_key=Truedoc=""内部机构号"" )Column('YXJGMC' String(200) nullable

sql server...-sql server如何实现16个以上联合主键

问题描述 sql server如何实现16个以上联合主键 现在一个表的联合主键有16个,需要添加一个联合主键,但是sql server最多只能16个主键,怎么能实现17个键的需求? 解决方案 在SQL Server中如何关于修改自增型主键的初始值获取SQL Server 主键有关sql server用int型主键的一些问题