Hibernate框架(二)
一级缓存
一级缓存:一级缓存:也称为session缓存.属于线程级别缓存. 该缓存本质其实就是一个集合.该集合被放置到了session对象中.所以也叫session缓存.因为session是线程级别的.所以该缓存属于线程级别缓存.
缓存作用:为了提高效率
提高修改效率:如果修改了查询到的对象,那么会自动同步到数据库,如果修改但是值没有发生变化,hibernate会”聪明”的不执行任何操作
原因:ResultSet会封装成对象,一式两份,分别放到session缓存中,以及缓存快照中.事务在提交时,会比对session缓存中的以及快照中的对象属性是否发生变化,如果发生变化那么执行修改.
代码:
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;
import cn.it.domain.Customer;
import cn.it.utils.HibernateUtils;
/**
* 演示一级缓存
* @author Administrator
*
*/
public class Demo01 {
/**
* 一级缓存在查询中的应用
*/
@Test
public void test1(){
//1.获得session
Session session = HibernateUtils.openSesseion();
//2.开启事务
Transaction tx = session.beginTransaction();
//3.执行操作
Customer c1 = session.get(Customer.class, 1l);
c1.setCust_name("李四");
Customer c2 = session.get(Customer.class, 1l);
//验证c2是否修改
System.out.println(c2.getCust_name());
//验证c1和c2是否是同一个Customer对象
System.out.println(c1 == c2);
//4.提交事务
tx.commit();
//5.关闭连接
session.close();
}
/**
* 一级缓存在修改中的应用
*/
@Test
public void test2(){
//1.获得session
Session session = HibernateUtils.openSesseion();
//2.开启事务
Transaction tx = session.beginTransaction();
//3.执行操作
Customer c1 = session.get(Customer.class, 1l);
c1.setCust_name("张三");
//如果最后改的值与数据库中的一致,将不会执行update
//如果最后改的值与数据库中的不一致,将会执行update
c1.setCust_name("李四");
//4.提交事务
tx.commit();
//5.关闭连接
session.close();
}
}
hibernate事务管理
事务
事务四大原则
A 原子性
C 一致性
I 隔离性
D 持久性
事务的并发问题
脏读:读取到别人修改但没有提交的数据
不可重复读:在两次连续读取过程中,读取到的数据不同,(修改)
幻|虚读:连续读取时,出现不同记录数(增删)
解决并发问题:设置隔离级别
(0001)1:读未提交:123
(0010)2:都已提交:23 (oracle默认)
(0100)4:可重复读:3 (mysql默认)
(1000)8:串行化
hibernate中修改事务隔离级别配置
在主配置文件中设置:
隔离级别(mysql默认为4)
hibernate事务的应用
- 事务在Service层管理
- 我们要确保Service层以及Dao层使用的是同一个session
使用hibernate框架也同样要确保Service层以及dao层曾使用的是同一个session。我们可以使用ThreadLoacl对象,将当前的session对象与线程绑定,从tl中获得的就是同一个。
hibernate中已经帮我们封装好了ThreadLocal对象,我们只需要调用getCurrentSession方法既是从ThreadLocal中获得与当前线程绑定的Session.
在主配置文件中设置:
<!--指定getCurrentSession获得session时与当前线程绑定>
<property name="hibernate.current_session_context_class">thread</property>
注意事项:
- getCurrentSession方法必须配置一行配置才能使用
- getCurrentSession方法获得的Session对象,会在事务结束后自动关闭,手动关闭会报错
代码:
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import cn.it.utils.HibernateUtils;
/**
* 演示getCurrentSession方法
* @author Administrator
*
*/
public class Demo01 {
@Test
public void test1(){
//1.getCurrentSession方法获得session
Session session1 = HibernateUtils.getCurrentSession();
Session session2 = HibernateUtils.getCurrentSession();
System.out.println(session1 == session2);
//2.openSession方法获得session
Session session3 = HibernateUtils.openSesseion();
Session session4 = HibernateUtils.openSesseion();
System.out.println(session3 == session4);
}
/**
* getCurrentSession获得的session对象,会在事务提交时自动关闭,不要手动关闭
*/
@Test
public void test2(){
//1.getCurrentSession方法获得session
Session session = HibernateUtils.getCurrentSession();
//2.开启事务
Transaction tx = session.beginTransaction();
//3.提交事务
tx.commit();
//4.关闭资源
session.close();//会报错
}
}
表关系
一对多|多对一
ORM
R关系数据库:
一对多|多对一:在表中表达,是在多的一方使用外键引用一的一方的主键
O对象(类):
一的一方使用集合来表达一对多关系,
多的一方直接引用一的属性,表达多对一关系
M映射文件:
<!-- 一对多关系映射
name 属性:填写集合的属性名
column属性:填写多的一方的外键名称
-->
<set name="linkman">
<key column="lkm_cust_id(外键名)"></key>
<one-to-many class="LinkMan" />
</set>
<!-- 多对一关系映射 -->
<many-to-one name="外键名" column="表名" class="类名" />
一对多|多对一的应用
Cascade:级联操作
cascade:级联操作
save-update:级联保存或级联更新
级联保存:
保存客户是级联保存客户下的联系人,将于客户管理的瞬时状态对象转换为持久化
级联更新:
自动更新与持久化状态客户关联的联系人对象,将与客户关联的游离状态的联系人对象转换为持久化状态
delete:级联删除
级联删除:删除客户是自动删除客户关联的联系人,将持久化状态的客户删除时,会级联把客户下的联系人删除(转换为瞬时状态)
all:save-update+delete
结论:cascade属性的性质属于减少开发时书写代码量的配置,属性如果不用完全可以靠写代码来代替,而且级联操作中尤其级联删除配置使用风险极大,不建议使用级联属性.
代码
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import cn.it.domain.Customer;
import cn.it.domain.LinkMan;
import cn.it.utils.HibernateUtils;
/**
* 级联操作
* @author Administrator
*
*/
public class Demo01 {
/**
* 添加一个客户,并添加客户下的两个联系人,级联保存
*/
@Test
public void test1(){
//1.获取session
Session session = HibernateUtils.openSesseion();
//2.开启事务
Transaction tx = session.beginTransaction();
//3.执行操作
//创建客户
Customer customer = new Customer();
customer.setCust_name("张三");
//创建联系人
LinkMan linkMan1 = new LinkMan();
linkMan1.setLkm_name("张三丰");
LinkMan linkMan2 = new LinkMan();
linkMan2.setLkm_name("张无忌");
//表达一对多或多对一关系
customer.getLinkMen().add(linkMan1);
customer.getLinkMen().add(linkMan2);
linkMan1.setCustomer(customer);
linkMan2.setCustomer(customer);
//将客户和联系人转换为持久化状态
session.save(customer);
// session.save(linkMan1);
// session.save(linkMan2);
//4.提交事务
tx.commit();
//5.关闭资源
session.close();
}
/**
* 级联更新
*/
@Test
public void test2(){
//1.获取session
Session session = HibernateUtils.openSesseion();
//2.开启事务
Transaction tx = session.beginTransaction();
//3.执行操作
//创建客户
Customer customer = new Customer();
customer.setCust_name("李四");
//创建联系人
LinkMan linkMan1 = new LinkMan();
linkMan1.setLkm_name("李世民");
linkMan1.setLkm_id(3l);
LinkMan linkMan2 = new LinkMan();
linkMan2.setLkm_name("李隆基");
linkMan2.setLkm_id(4l);
//表达一对多或多对一关系
customer.getLinkMen().add(linkMan1);
customer.getLinkMen().add(linkMan2);
linkMan1.setCustomer(customer);
linkMan2.setCustomer(customer);
//将客户和联系人转换为持久化状态
session.save(customer);
//4.提交事务
tx.commit();
//5.关闭资源
session.close();
}
/**
* 级联删除
*/
@Test
public void test3(){
//1.获取session
Session session = HibernateUtils.openSesseion();
//2.开启事务
Transaction tx = session.beginTransaction();
//3.执行操作
//获取客户
Customer customer = session.get(Customer.class, 4l);
//将客户和联系人转换为瞬时状态
session.delete(customer);
//4.提交事务
tx.commit();
//5.关闭资源
session.close();
}
}
inverse属性
问题:sql语句打印冗余
分析:
关系配置了两次.
1对多 为客户添加联系人
多对1 为联系人添加所属客户
解决方案:
1. 只有多的一方维护关系,这一方一定是多的一方(外键所在方维护关系)
2. 配置inverse属性,让一的一方(客户)不再维护关系.
inverse属性,是否放弃维护关系,ture:放弃,false(默认值):维护.
一对多中可以使用inverse属性放弃维护关系,多对一中不能放弃维护关系.
结论:为了避免sql语句打印冗余,在开发时往往选择放弃一的一方的关系维护,配置后要注意,表达关系要由多的一方进行维护.
代码:
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import cn.it.domain.Customer;
import cn.it.domain.LinkMan;
import cn.it.utils.HibernateUtils;
/**
* 表的关系:一对多|多对一
* 表之间的关系维护
* @author Administrator
*
*/
public class Demo02 {
/**
* 维护关系重复导致冗余sql语句
* 分析:关系配置了两次
* 为客户添加联系人
* 为联系人添加所属客户
* 解决方案:只由外键所在方维护关系,一的一方不进行维护
*/
@Test
public void test1(){
//1.获取session
Session session = HibernateUtils.openSesseion();
//2.开启事务
Transaction tx = session.beginTransaction();
//3.执行操作
//创建客户
Customer customer = new Customer();
customer.setCust_name("张三");
//创建联系人
LinkMan linkMan1 = new LinkMan();
linkMan1.setLkm_name("张三丰");
LinkMan linkMan2 = new LinkMan();
linkMan2.setLkm_name("张无忌");
//表达一对多或多对一关系
// customer.getLinkMen().add(linkMan1);
// customer.getLinkMen().add(linkMan2);
linkMan1.setCustomer(customer);
linkMan2.setCustomer(customer);
//将客户和联系人转换为持久化状态
session.save(customer);
session.save(linkMan1);
session.save(linkMan2);
//4.提交事务
tx.commit();
//5.关闭资源
session.close();
}
/**
* 维护关系重复导致冗余sql语句
* 分析:关系配置了两次
* 为客户添加联系人
* 为联系人添加所属客户
* 解决方案:配置inverse属性,让一的一方不再维护关系
*/
@Test
public void test2(){
//1.获取session
Session session = HibernateUtils.openSesseion();
//2.开启事务
Transaction tx = session.beginTransaction();
//3.执行操作
//创建客户
Customer customer = new Customer();
customer.setCust_name("张三");
//创建联系人
LinkMan linkMan1 = new LinkMan();
linkMan1.setLkm_name("张三丰");
LinkMan linkMan2 = new LinkMan();
linkMan2.setLkm_name("张无忌");
//表达一对多或多对一关系
customer.getLinkMen().add(linkMan1);
customer.getLinkMen().add(linkMan2);
linkMan1.setCustomer(customer);
linkMan2.setCustomer(customer);
//将客户和联系人转换为持久化状态
session.save(customer);
session.save(linkMan1);
session.save(linkMan2);
//4.提交事务
tx.commit();
//5.关闭资源
session.close();
}
}
事务操作
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import cn.it.domain.Customer;
import cn.it.domain.LinkMan;
import cn.it.utils.HibernateUtils;
/**
* 表的关系:一对多|多对一
* @author Administrator
*
*/
public class Demo03 {
/**
* 外键值置为null,联系人与客户解除关系
*/
@Test
public void test1(){
//1.获取session
Session session = HibernateUtils.openSesseion();
//2.开启事务
Transaction tx = session.beginTransaction();
//3.执行操作
//获得id为5的联系人
LinkMan linkMan = session.get(LinkMan.class, 5l);
//设置联系人不属于任何客户
linkMan.setCustomer(null);
//4.提交事务
tx.commit();
//5.关闭资源
session.close();
}
/**
* 取得客户所属联系人
*/
@Test
public void test2(){
//1.获取session
Session session = HibernateUtils.openSesseion();
//2.开启事务
Transaction tx = session.beginTransaction();
//3.执行操作
//获得id为5的客户
Customer customer = session.get(Customer.class, 2l);
//取出id为5的客户的联系人
Set<LinkMan> linkMen = customer.getLinkMen();
for (LinkMan linkMan : linkMen) {
System.out.println(linkMan.getLkm_name());
}
//4.提交事务
tx.commit();
//5.关闭资源
session.close();
}
/**
*修改联系人所属的客户
*/
@Test
public void test3(){
//1.获取session
Session session = HibernateUtils.openSesseion();
//2.开启事务
Transaction tx = session.beginTransaction();
//3.执行操作
//获得id为5的联系人
LinkMan linkMan = session.get(LinkMan.class, 5l);
//新建id为3的客户
Customer customer = new Customer();
customer.setCust_id(3l);
//修改联系人客户
linkMan.setCustomer(customer);
//4.提交事务
tx.commit();
//5.关闭资源
session.close();
}
}
多对多
ORM
R 关系数据库
新建一张第三方表(中间表),关联有关系的两张表
O 对象
分别用集合引用另一个对象
M 映射文件
两个配置文件格式相同,值进行更换
<!-- 多对多文件配置 -->
<set name="集合属性名" table="中间表名">
<key column="引用'我'的外键名"></key>
<many-to-many class="与谁是多对多的类名" column="引用'他'的外键名"></many-to-many>
</set>
操作
问题:如果员工以及员工下的角色,导致向中间表插入重复记录而报错.
解决:由一方放弃维护关系
1.不写其中一方维护关系的代码.
2.配置inverse=true,强制让一方放弃维护关系.
结论:在开发中为了避免多对多两方多维护关系造成报错,一定需要选择一方来放弃维护关系,哪一方放弃要看业务方向,先存着的一方放弃维护关系.
cascade属性:级联操作
save-update:级联保存或更新
delete:级联删除
all:二合一