组件映射
1 项目:hibernate_1100_component
2 对象关系:一个对象是另外一个对象的一部分
3 数据库表:一张表
4 annotation: @ Embeddable @Embbeded
对象模型
Husband(id,name,wife)
Wife(name,age)
Annotation:
在Husband的wife属性上建立注解
@Embedded 表明该对象是从别的位置嵌入过来的,是不需要单独映射的表.
这种方式生成的表为husband(id,name,wifename,wifeage),不会生成wife表.
@AttributeOverride注解需要写在getWife方法上,可以重新指定生成的Wife类组件生成的字段名,例如:Husband与Wife两个类中都有name字段,这样在生成表的时候会有冲突,此时采用@AttributeOverride注解可以指定Wife类中的name属性对应新的字段名—“wifename”,不过@AttributeOverride注解不常用,因为有更好的解决方法. 1:不要在组件的两个映射类中写同名属性;2:如果真的有重复,那么可以在分类中(此处为Wife类)的重复名称的属性上使用如下内容以指定新的字段名:
@Column(name="wifename")
public String getName() {
return name;
}
另外,@ Embeddable注解好像是写在分类(Wife类)的类名前的,不过好像不写也行
@Embeddable
public class Wife {… …}
5 xml: 使用<component,例如:
<class name="Husband" >
<id name="id">
<generator class="native"/>
</id>
<property name="name"></property>
<component name="wife">
<property name="wifeName"/>
<property name="wifeAge"/>
</component>
</class>
多对一与一对多
1 多对一单向关联
a)项目名称:hibernate_1200_many2one_uni
实体模型(User多对一Group)
User(id,name,group)多
Group(id,name)一
b)数据库表设计:在多方加外键
错误做法:在一方加冗余
perosnid | person name | dreamid |
1 | zhangsan | 1 |
1 | zhangsan | 2 |
dreamid | dreamdescr |
|
1 | earn money |
|
2 | eat a lot |
|
c)annotaion: @Many2One
只需要在多的一端User属性group进行注解配置
@ManyToOne //多对一关联 User是多的一方 Group是一的一方
@JoinColumn(name="groupid") //指定User表中生成与Group对应的字段名
public Group getGroup() {
return group;
}
d)xml: <many-to-one
<many-to-one name="group" column="groupId" />
标签会在”多”的一端添加外键,相当于在数据库中添加外键生成的表为user(id,name,groupid),t_group(id,groupname)
属性cascade
<many-to-one name="group" column="groupid" cascade="all"/>
取值all,none,save-update,delete,对象间的级联操作,只对增删改起作用.
在存储时User时,设置了cascade="all"会自动存储相应的t_group.而不用管user关联的对象(通常情况下会优先存储关联的对象,然后再存储user)
2 一对多单向关联
a) 项目名称:hibernate_1300_one2many_uni
模型(group一对多user)
Group(id,name,users)一
User(id,name)多
设计时在一的这一端存在着多的集合,生成的数据库表通常是在多的一端生成外键.
Set<User> users = new HashSet<User>();
b)类:在一的一方存在多方的集合
c) 数据库表同上
d)Annotation:@One2Many
在一的这一端Group端users属性上进行注解配置
@OneToMany //一对多关联 Group是一的一方 User是多的一方
@JoinColumn(name="groupid") //指定User表中生成与Group对应的字段名 注意此处与多对一配置方式不同
Hibernate默认将OneToMany理解为ManyToMany的特殊形式,如果不指定生成的外键列@JoinColumn(name="groupId"),则会默认生成多对多的关系,产生一张中间表。
e)xml:<set <one2many
XML配置中配置一的那一端Group
<class name="com.hibernate.Group" table="t_group">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="users">
<key column="groupId"/>指定生成外键字段的名字
<one-to-many class="com.pojo.User"/>
</set>
</class>
3 一对多(多对一)双向关联
一对多与多对一的双向关联是同一种情况.
关系模型(group一对多user)
Group(id,name,users)一
User(id,name,group)多
Set<User> users=new HashSet<User>()
配置规则:一般以多的一端为主,先配置多的一端
在多的一端User端配置group
@ManyToOne
@JoinColumn(name="groupid")
在一的一端Group端配置时,在users只需要加个mappedBy="groupid"
@OneToMany(mappedBy="group")
XML配置
Group中
<set name="users">
<key column="groupId"/>
<one-to-many class="com.hibernate.User"/>
</set>
在User中
<many-to-one name="group" column="groupId"/>
务必确保在多的一端生成的生成的外键和一的一方生成的外键的名字相同,都为groupId.
如果名字不同则会在多的一端生成多余的外键
多对多
1 单向关联:
a) 项目:hibernate_1500_many2many_uni
关系模型(Teache多对多Student),从Teacher这一端能关联到students.
Teacher(id,name,students)多
Student(id,name)多
Set<Student> students=new HashSet<Student>()
在Teacher那一端配置
b)例如:老师和学生的关系,老师需要知道自己教了哪些学生
c)数据库:生成中间表
d)Annotation:@Many2Many
Teacher类中写:
private Set<Student> students = new HashSet<Student>();
@ManyToMany //多对多关联 Teacher是主的一方 Student是附属的一方
@JoinTable(name="t_s", //指定中间表表名
joinColumns={@JoinColumn(name="teacherid")},//本类主键在中间表生成的
对应字段名
inverseJoinColumns={@JoinColumn(name="studentid")}//对方类主键在中间表
生成的对应字段名
)
public Set<Student> getStudents(){……}
e)XML:<many2many
<class name="com.xxx.Teacher">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="students" table="t_s">table定义中间表的表名
<key column="teacher_id"></key>
<many-to-many class="com.xxx.Student" column="student_id"/>
</set>
</class>
2 双向关联:
a)项目:hibernate_1600_many2many_bi
多对多双向配置只需要在两端类进行配置就才可以.
关系模型(Teache多对多Student)
Teacher(id,name,students)多
Student(id,name,teachers)多
Set<Student> students = new HashSet<Student>()
Set<Teacher> teachers = new HashSet<Teacher>();
b)老师知道自己教了哪些学生,学生也知道教自己的有哪些老师
c)数据库:生成中间表
d) Annotation:
在Teacher这一端的students上配置
@ManyToMany
@JoinTable(name="t_s",
joinColumns={@JoinColumn(name="teacher_id")},
inverseJoinColumns={@JoinColumn(name="student_id")}
)
在Student一端的teachers只需要配置
@ManyToMany(mappedBy="students")
注意:mappedBy 与 @JoinTable等一类的配置要分开,不然表字段可能乱
e)XML:
XML配置方式:两端配置一样,注意表名和生成的中间表的字段属性名要一致
Teacher那一端配置
<set name="students" table="t_s">
<key column="teacher_id"/>
<many-to-many class="com.xxx.Student" column="student_id"/>
</set>
在Student那一端配置
<set name="teachers" table="t_s">
<key column="student_id"></key>
<many-to-many class="com.xxx.Teacher" column="teacher_id"/>
</set>
生成的数据库表和上面是一样的
关联关系中的CRUD_Cascade_Fetch
1 hibernate_1700_one2many_many2one_bi_crud
2 设定cascade以设定在持久化时对于关联对象的操作(CUD,R归Fetch管)
3 cascade仅仅是帮我们省了编程的麻烦而已,不要把它的作用看的太大
a) Cascade的属性是数组格式,指明做什么操作的时候关联对象是绑在一起的
b) refresh = A里面需要读B改过之后的数据
cascade={CascadeType.ALL}
CascadeType取值
ALL Cascade all operations所有情况
MERGE Cascade merge operation合并(merge=save+update)
PERSIST Cascade persist operation存储 persist()
REFRESH Cascade refresh operation刷新
REMOVE Cascade remove operation删除
4 铁律:双向关系在程序中要设定双向关联
5 铁律:双向mappedBy
6 fetch
a) 铁律:双向不要两边设置Eager(会有多余的査询语句发出)
b) 对多方设置fetch的时候要谨慎,结合具体应用,一般用Lazy不用eager,特殊情况(多方数量不多的时候可以考虑,提高效率的时候可以考虑)
@OneToMany(mappedBy="group",
cascade={CascadeType.ALL}, //控制增删改(即CUD)
fetch=FetchType.EAGER //控制查询(即R) EAGER值代表取出关联 LAZY值为不取关联
//多的一方fetch取值默认为LAZY 一的一方默认为EAGER
)
另外:如果User类(即多的一方)中设置fetch=FetchType.LAZY 则在调用多(即Group)的对象值的时候
类似延迟加载 即需要在commit();之前 session还存在时调用 如:
System.out.println(user.getGroup().getName());
session.getTransaction().commit();
7 Update时@ManyToOne()中的cascade参数关系
session.beginTransaction();
User user = (User)session.load(User.class,1);
//user对象属性改变 事务commit时自动判断与数据库原有数据不同 可自动update
//此时的update与@ManyToOne()中的cascade或fetch参数取值无关
user.setName("user1");
user.getGroup().setName("group1");
session.getTransaction().commit();
如果user改变在commit()之后 且想要执行Update方法时 user与group表同时更新则,则User类的cascade={CascadeType.ALL},并在程序中写如下代码:
session.beginTransaction();
User user = (User)session.get(User.class,1);
session.getTransaction().commit();
user.setName("user1");
user.getGroup().setName("group1");
Session session2 = sessionFactory.getCurrentSession();
session2.beginTransaction();
session2.update(user);
session2.getTransaction().commit();
8 Delete时@ManyToOne()中的cascade关系
如果User及Group类中均设为@ManyToOne(cascade={CascadeType.All}),那么在执行如下:
session.beginTransaction();
User user = (User)session.load(User.class,1);
session.delete(user);
session.getTransaction().commit();
注意:此处删除的是 多对一(即User对Group) 中的“多”的一方(User类)
会删除user及user对应的group,再反向对应group的user都会删除,原因就是设置了@ManyToOne(cascade={CascadeType.All})
三种方法可避免全部删除的情况:
1. 去掉@ManyToOne(cascade={CascadeType.All})设置;
2. 直接写Hql语句执行删除;
3. 将user对象的group属性设为null,相当于打断User与Group间的关联,代码如下
session.beginTransaction();
User user = (User)session.load(User.class,1);
user.setGroup(null);
session.delete(user);
session.getTransaction().commit();
注意:如果删除的是 多对一中的“一”的一方(Group类)时,如果使用第3种方式(user属性设为null)来打断两个对象间的关联的话,代码与之前不同,如下:
session.beginTransaction();
Group group = (Group)session.load(Group.class,1);
//循环将group中的set集合下的各个user对象设为null
//相当于先将数据库中user表中与group表关联的字段(即groupid)设为null
for(User user :group.getUsers()){
System.out.println(user.getName());
user.setGroup(null);
}
//再将group的set集合设为null,相当于将group表中与user表关联的字段(即userid)设为null
//此句的前提是user表中的关联字段(groupid)已经为null,如没有则相当于破坏了一对多关联,会报错
group.setUsers(null);
session.delete(group);
session.getTransaction().commit();
9 O/RMapping 编程模型
a) 映射模型
- i. jpa annotation(java提供的annotation配置--常用)
- ii. hibernate annotation extension(Hibernate扩展的annotation配置--较少用)
- iii. hibernate xml(Hibernate的xml配置方式--常用)
- iv. jpa xml(java提供的xml配置--较少用)
b) 编程接口
- i. Jpa(不常用)
- ii. hibernate(现在用)
c) 数据査询语言
- i. HQL
- ii. EJBQL(JPQL)
10 要想删除或者更新先做load,除了精确知道ID之外
11 如果想消除关联关系,先设定关系为null.再删除对应记录,如果不删记录,该记录变成垃圾数据
12 练习:多对多的CRUD
teacher | student |
t1 | s1 |
t1 | s2 |
t2 | s1 |
t2 | s2 |