一、session的创建
Session是由SessionFactory负责创建的,而SessionFactory的实现是线程安全的,多个并发的线程可以同时访问一个SessionFactory并从中获取Session实例,但Session不是线程安全的。
每次openSession,产生的都是一个新的session,相当于创建一个新的连接。但是有很多时候,并不希望这样。比如在淘宝购物,在付账的一瞬间,至少有三件事情发生,转账,仓库数据变化,购物历史记录。而这三件事有必须在同一事务下。自然我们会联想都ThreadLocal<Session> 来解决这个问题。
Hibernate提供了getCurrentSession()方法来解决这一问题,详细看ThreadLocalSessionContext类源码
思考:为什么ThreadLocal(context)里放的是Map,且Map的key是sessionFactory,value是session?
1.一个sessionFactory代表一个数据库连接,这样Map的key存放sessionFactory肯定也就是一个(对于一个数据库),自然也就是一个session,而使用Map也就是为了多个数据库连接。
2.在web操作时希望 request 和 response 是一个连接,这样设计保证了这点,无论怎么误操作,即使又新建了一个session,但是sessionfatory不变,也只会把原理的session覆盖,还是保证了在一个session里。
这样的做法,起到了一个双保险的作用,IBM以前就是ThreadLocal<Session>。
getCurrentSession使用
在hibernate.cfg.xml中添加
<property name="current_session_context_class">thread</property>
注意:如果用该方法(当前线程先产生session),CRUD必须都在事务下进行,在transaction.commit()时,session自动关闭。
缺点:把session和transaction绑定在一起了.在transaction提交之后,再想进行数据库操作就不行了(工作流)
Spring与Hibernate结合后,就把这个提交方式改了,事务提交与session关闭分开。
二、one2many 一对多关系映射
2.1单向
classes类
public class Classes implements Serializable{
private Long cid;
private String name;
private Set<Student> students;
}
student类
public class Student implements Serializable{
private Long sid;
private String name;
}
映射文件
<class name="Classes" table="CLASSES">
<id name="cid">
<generator class="native"></generator>
</id>
<property name="name"></property>
<!--
set元素针对的就是Classes类中的Set属性
cascade 级联操作
null 默认值
save-update
在保存classes对象的时候,针对student进行保存或者更新的操作
在更新classes对象的时候,针对student进行保存或者更新的操作
all
delete
inverse 关系操作
default:classes维护classes与student之间的关系
true: classes不维护classes与student之间的关系
false: classes维护classes与student之间的关系
-->
<set name="students" cascade="save-update" inverse="true">
<!-- key:外键,告诉hibernate通过cid来建立classes与student的关系 -->
<key column="cid"></key>
<one-to-many class="Student"/>
</set>
</class>
<class name="Student" table="STUDENT">
<id name="sid">
<generator class="native"></generator>
</id>
<property name="name"></property>
</class>
测试类,主要看hibernate发出的sql语句
public class One2manyTest {
private Session session;
private Transaction transaction;
@Before
public void init(){
session = HibernateUtils.openSession();
transaction = session.beginTransaction();
}
/**
* 一对多的单向操作
*/
@Test
public void testSaveClass_cascade_SaveStudent(){
Classes classes = new Classes();
classes.setName("软件");
Set<Student> students = new HashSet<Student>();
Student student = new Student();
student.setName("A");
students.add(student);
classes.setStudents(students);
session.save(classes);//需要设置cascade
}
/**
* sessin.flush的时候
* 1、检查一级缓存中所有的持久化状态的对象
* 判断发出insert语句或者update语句
* 2、检查所有的持久化对象的关联对象
* 如果关联对象是由临时状态转化过来的,则对关联对象发出insert语句
* 如果关联对象是从数据库中提取出来的,则对照副本,决定是否发出update语句
*/
@Test
public void testUpdateClass_cascade_UpdateStudent(){
Classes classes = (Classes) session.get(Classes.class, 3L);
Set<Student> students = classes.getStudents();
for(Student student : students){
student.setName("AA");
}
session.update(classes);
}
/**
* 维护关联关系
* 更新班级 级联 保存学生
*/
@Test
public void testUpdateClass_cascade_SaveStudent(){
Classes classes = (Classes)session.get(Classes.class, 3L);
Student student = new Student();
student.setName("cc");
//建立班级与学生之间的关系
classes.getStudents().add(student);
//看发出sql语句
/**
* Hibernate:
update
STUDENT
set
cid=?
where
sid=?
维护关联关系:inverse:false由本身维护,true由对方维护,默认false
inverse设置为true,有Student维护关联关系,就不发送最后的update语句
*/
}
@After
public void destory(){
transaction.commit();
session.close();
}
}
2.2双向
public class Student implements Serializable{
private Long sid;
private String name;
private Classes classes;
}
<class name="Student" table="STUDENT">
<id name="sid">
<generator class="native"></generator>
</id>
<property name="name"></property>
<!--
name:Student类属性
column:外键
-->
<many-to-one cascade="save-update" name="classes" column="cid" class="Classes">
</many-to-one>
</class>
测试类
public class One2manyTest {
private Session session;
private Transaction transaction;
@Before
public void init(){
session = HibernateUtils.openSession();
transaction = session.beginTransaction();
}
/**
* Hibernate:
insert
into
STUDENT
(name, cid)
values
(?, ?)
连带cid直接插进去,所以如果用多的一个方,来维护关系,操作就是本身,没有维护外键一说
*/
@Test
public void testSaveStudent_cascade_SaveClasses(){
Student student = new Student();
student.setName("haha");
Classes classes = new Classes();
classes.setName("软件1302");
//通过学生建立关系
student.setClasses(classes);//注意给Student设置cascade
session.save(student);
}
/**
* 把sid为3的学生,从cid为3的班级转到cid为4的班级
* Hibernate:
update
STUDENT
set
name=?,
cid=?
where
sid=?
*/
@Test
public void testTransformClasses(){
Student student = (Student) session.get(Student.class, 3L);
Classes classes = (Classes) session.get(Classes.class, 4L);
student.setClasses(classes);
//session.update(student);
}
//移除一个班级的学生
@Test
public void testRemoveStudentFromClasses1(){
Student student = (Student) session.get(Student.class, 7L);
Classes classes = (Classes) session.get(Classes.class,3L);
Set<Student> students = classes.getStudents();
students.remove(student);//并没有删除,这里需要注意,因为此时是由多的一方维护关联关系,一的一方维护失效
session.update(classes);
}
@Test
public void testRemoveStudentFromClasses2(){
Student student = (Student) session.get(Student.class, 7L);
Classes classes = (Classes) session.get(Classes.class,3L);
student.setClasses(null);
}
@After
public void destory(){
transaction.commit();
session.close();
}
}
总结:
inverse与cascade的关系
cascade指的是级联操作,操作的是一般属性,指的是对象与对象的操作
inverse指的是关系操作,针对的是外键
一对的多的双向:当多的一方维护关系时,不会发出更新关系的update语句,而一的一方维护关关系时需要发出维护关系的update语句,一般情况下,多的一方维护关系效率比较高。
一对多描述的是对象与集合之间(一对多)的关系或者对象与对象(多对一)之间的关系。