hibernate基础概念理解
持久化类
什么是持久化类?
持久化:将内存中的一个类对象持久化到数据库中的过程,hibernate框架就是用来进行持久化的框架
持久化类:一个java对象类与数据库表建立了映射关系,则这个类在hibernate中称为持久化类
可以理解为:持久化类=java类+映射文件
持久化类的编写规则
- 对持久化类提供一个无参构造函数 : hibernate底层需要使用反射生成实例
- 类的属性要私有,对私有属性提供public的get和set方法 : hibernate中获取、设置对象的值
- 对持久化类提供一个唯一的表示与数据库的主键相对应 : java中通过对对象的地址来区分是否为同一个对象,数据库中通过主键确定是否为同一个记录,在hibernate中通过持久化类的OID属性区分是否为同一个对象。
- 持久化类的属性的类型尽量使用包装类型,而不要使用基本数据类型 : 因为基本数据类型默认值为0,那么0就会有很多的歧义。(即不知道其代表的实际意义:是忘记设置值了,还是数据本身就是0)而包装类型默认值为null,这样如果是这个属性的值忘记插入了就是null,而0就表示了实际意义的0。
- 持久化类不要使用final修饰 : 延迟加载本身是hibernate的一个优化手段。返回的是一个代理对象(javassist可以独一没有实现接口的类产生代理-----使用了非常底层的字节码增强技术,继承这个类来进行代理的)如果这个类使用了final,就无法被继承,就不能产生代理对象了,延迟加载就失效了,那么load方法和get方法就没有区别了,全部变成get方法的功能了
主键生成策略
主键的分类:
- 自然主键 主键本身就是表中的一个字段(实体中的某个具体的属性)
- 创建一个人员表,人员都会有一个身份证号,使用省份证号作为主键,这种主键就是自然主键
- 代理主键 主键本身不是表中必须的一个字段(不是实体中的某个具体的属性)
- 创建一个人员表,没有使用省份证号作为主键,用了一个与表不相关的字段ID,(PNO)。这种主键就是代理主键
在实际开发中,尽量使用代理主键
- 一旦自然主键参与到业务逻辑中,后期有可能需要修改源代码
- 好的程序设计妈祖OCP原则:对程序的扩展是open的。对修改源码是close的
主键的生成策略
程序开发过程中,不允许用户去生成主键,以避免出现主键重复的情况,一般将主键交给数据库,手动编写
程序进行设置。在hibernate中位列减少程序编写,提供了许多种主键的生成策略
- increment
hibernate中提供的自动增长机制,用于为 long , short 或者 int 类型生成 唯一标识。在单线程程序中使用
首先发送一条语句:select max(id) from 表;然后让 id+1 作为下一条记录的主键
- identity
适用long , short 或者 int 类型的主键,使用的是数据库底层的自动增长机制,适用于自动增长机制数据库(mysql、mssql)但是oracle是没有自动增长,而是通过序列实现的
- sequence
适用于 long , short 或者 int 类型的主键,采用的是序列的方式,(oracle支持序列)
- uuid
适用于字符串类型的主键,使用hibernate中的随机方式生成的字符串主键
- native
本地策略,可以在identity和sequence之间自动切换
- assigned
hibernate放弃外键的管理,需要通过手动编写程序或用户自己设置
- foreign
外部的,在一对一关联映射的情况下使用。(了解)
持久化类的三种状态
瞬时态
这种对象没有唯一标识OID,没有被session管理
持久态(最重要的就是了解这个状态的情况)
这种对象有唯一标识OID,被session管理
- 持久化类的持久态对象可以自动更新数据库
脱管态
这种对象有唯一标识OID,没有被session管理
区分这三种状态
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
Customer customer = new Customer();//瞬时态:没有唯一标识OID,没有被session管理
customer.setCust_name("bbb");
Serializable id = session.save(customer);//持久态:有唯一标识OID,被session管理
/*customer.setCust_industry(cust_industry);
session.get(Customer.class, id);*/ // 这中间一直都处于持久态
transaction.commit();
session.close();//到这里session就销毁了
System.out.println(customer);//脱管态:有唯一标识OID,但是没有被session管理
持久化类的状态转换
- 瞬时态对象
- 获得 Customer customer = new Customer();
- 状态转换 瞬时==》持久 save(Object obj);saveOrUpdate(Object obj); 瞬时==》脱管 customer.setCust_id(1L);
- 持久态对象
- 获得 get(); load(); find(); iterate(); Customer customer = session.get(Customer.class,1L);
- 状态转换 持久==》瞬时 delete(); 持久==》脱管 session.close(); session.clear(); session.evict(Object obj);
- 脱管态对象
- 获得 Customer customer = new Customer(); customer.setCust_id(1L);这样可以将customer看作一个脱管态对象
- 状态转换 脱管==》持久 update(); saveOrUpate(); 脱管==》瞬时 cutomer.setCust_id(null);
持久态对象的特性:可以自动更新数据库
数据库中原来的值
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
//获取持久态对象
Customer customer = session.get(Customer.class, 1L);
customer.setCust_name("great");
//session.update(customer);//此处可以省略,一样可以做到更新数据库
transaction.commit();
session.close();
运行上面的代码之后
通过持久态对象更新数据,先查询出数据库中的对象,若设置的对象数据与数据库中的数据不同就会自动执行update
再次执行上面的代码
此时就不会在执行update了
而数据库中也不会有任何变化
为何hibernate会如此智能呢?
因为其底层的原理依赖了hibernate的一级缓存
hibernate的缓存
什么是缓存
缓存:一种优化的方式,将数据存入到内存中,使用时直接从缓存中获取,不用通过存储源
hibernate框架中提供了优化手段:缓存、抓取策略。hibernate中提供了两种缓存机制:一级缓存、二级缓存
hibernate的一级缓存(session级别的缓存,即一级缓存的生命周期与session是一样的)
一级缓存中是由session中的一系列的java集合构成,而且一级缓存是自带的,不可卸载。
hibernate的二级缓存是sessionFactory级别的缓存,是需要配置的缓存,hibernate默认不开启此缓存,而且我们开发一般不用此缓存,一般用Redis等替代。
证明一级缓存的存在
/**
*证明一级缓存的存在
*/
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
Customer customer1 = session.get(Customer.class, 1L);//发送sql语句
System.out.println(customer1);
Customer customer2 = session.get(Customer.class, 1L);//不发送sql语句
System.out.println(customer2);
/*这里按理说会查询两次,但看看下下面的控制台输出结果,
* 查询只查询了一次,输出了两次,而且获得的两个对象是同一个对象
* 第一次查询时 会发送sql语句,第二次不会,而是直接从缓存中获取,获取到的当然是同一个对象
* 因为get方法会先到缓存中查看有没有要查询的对象,如果没有才会到数据库中查询,查到后会放入缓存中一份
*/
System.out.println(customer1==customer2);
transaction.commit();
session.close();
先执行save,再通过save返回的serializable查询也一样
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
Customer customer = new Customer();
customer.setCust_name("jack");
Serializable id = session.save(customer);
Customer customer2 = session.get(Customer.class, id);//不发送sql语句
System.out.println(customer2);
transaction.commit();
session.close();
hibernate的一级缓存的结构
一级缓存中的特殊区域:快照区 当数据首次进入缓存时会进行一次快照,即将数据备份一份到快照区,当再次有数据进入缓存时(缓冲区的值会变,但快照区不会变)会将缓存中的数据与快照区进行对比,如果相同,则不进行数据库操作,否则将随数据库进行操作(或是查询数据,或是插入数据)
hibernate事务管理
什么是事务
事务:指逻辑上的一组操作,组成这组操作的各个逻辑单元要么全都成功,要么全都失败
事务特性
原子性 :事务不可分割
一致性 :事务执行的前后数据的完整性保持一致
隔离性 :一个事务执行的过程中,不应受到其他事务的干扰
持久性 :事务执行完成后,数据就持久到数据库中
如果不考虑隔离性,会引发安全问题
读的问题:脏读 :一个事务读到另一个事务未提交的数据
不可重复读 :一个事务读到另一个事务已经提交的update数据,导致在前一个事务多次查询结果不一致
虚读 :一个事务读到另一个事务已经提交的insert数据,导致在前一个事务多次查询结果不一致
读问题的解决:
设置事务隔离级别
read uncommited :以上问题都会发生
read uncommited :解决脏读,但后两种有可能发生
repeatable read :解决脏读和不可重复读,但虚读有可能发生
serializable :解决所有问题
我们一般使用第二个和第三个隔离级别 oracle默认用的read uncommited mysql默认用的是repeatable read
hibernate中设置隔离级别
在hibernate的核心配置中配置属性:
<!-- 事务隔离级别-->
<!-- hibernate 中的隔离级别使用数字表示 -->
<!-- hibernate.connection.isolation -4 -->
<!-- 1 read uncommitted isolation -->
<!-- 2 read committed isolation -->
<!-- 4 repeatable read isolation -->
<!-- 8 serializable isolation -->
<property name="hibernate.connection.isolation">4</property>
hibernate解决service的事务管理
首先,我们要修改一下我们的hibernate工具类:添加了一个getCurrentSession()方法
/**
* Hibernate的工具类
*/
public class HibernateUtils {
public static final Configuration cfg;
public static final SessionFactory sf;
static{
cfg = new Configuration().configure();
sf = cfg.buildSessionFactory();
}
public static Session openSession(){
return sf.openSession();
}
public static Session getCurrentSession() {
//默认此方法是不能用的,我们需要在核心配置文件中配置当前线程绑定的Session
return sf.getCurrentSession();
}
}
配置当前线程绑定的Session
<!-- 配置当前线程绑定的Session -->
<property name="hibernate.current_session_context_class">thread</property>
使用getCurrentSession后不能使用session.close();因为只要线程结束,session就会自动释放
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
Customer customer = new Customer();
customer.setCust_name("Tom");
session.save(customer);
transaction.commit();
//session.close();//不能写了,线程结束后会自动关闭session,再次关闭会报错
hibernate其他API
Query
Query接口用于接收HQL ,查询多个对象
HQL(Hibernate Query Language)Hibernate查询语言,这种语言与sql的语法及其类似,是面向对象的查询语言
查询所有
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//通过session获得Query接口
String hql = "from Customer";
Query query = session.createQuery(hql);
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
数据库表数据
输出结果
带条件的查询
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//通过session获得Query接口
String hql = "from Customer where cust_name like ?";//注意这里的属性名是类中的属性,不是数据库中的字段
Query query = session.createQuery(hql);
//query.setString(0, "%a%");//这里设置?的值,第一个参数是表示从0开始,这里是设置String类型
query.setParameter(0, "%a%");//不用管类型了
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
运行结果
分页查询
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
String hql = "from Customer ";//分页查询不用limit,limit在oracle中不适用
Query query = session.createQuery(hql);
query.setFirstResult(0);//代表limit的第一个参数
query.setMaxResults(2);//每页显示多少条记录
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
这样写,以后如果数据库改变了,如,变成了oracle数据库,那么hibernate就会自动将查询语句改成oracle的形式
Critria:条件查询
criteria:QBC(Query By Criteria)这种更适合进行一些条件检索 更加面向对象的一种查询方式
查询所有
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//通过session获取Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
List<Customer> list = criteria.list();//完全没有查询语句
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
查询结果
条件查询
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
Criteria criteria = session.createCriteria(Customer.class);
//设置条件
// criteria.add(Restrictions.like("cust_name", "%a%"));//完全面向对象实现
criteria.add(Restrictions.like("cust_name", "a",MatchMode.ANYWHERE));//这里还可以加第三个参数
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
查询结果
分页查询时跟Query一样,criteria也有这两个方法
SQLQuery
SQlQuery:用于接收sql。只有在特别复杂的情况下使用SQL。(比如有8、9个表进行关联时)我们开发一般用到前两个就足够了
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 接收HQL:Hibernate Query Language 面向对象的查询语言
/*Query query = session.createQuery("from Customer");
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}*/
// 接收SQL:
SQLQuery query = session.createSQLQuery("select * from cst_customer");
List<Object[]> list = query.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
tx.commit();
session.close();