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方法的功能了

主键生成策略

主键的分类:

  • 自然主键   主键本身就是表中的一个字段(实体中的某个具体的属性)
  1. 创建一个人员表,人员都会有一个身份证号,使用省份证号作为主键,这种主键就是自然主键
  • 代理主键    主键本身不是表中必须的一个字段(不是实体中的某个具体的属性)
  1. 创建一个人员表,没有使用省份证号作为主键,用了一个与表不相关的字段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管理

持久化类的状态转换

  • 瞬时态对象
  1. 获得     Customer customer = new Customer();
  2. 状态转换    瞬时==》持久    save(Object obj);saveOrUpdate(Object obj);                                                                                                                瞬时==》脱管    customer.setCust_id(1L);
  • 持久态对象
  1. 获得   get(); load(); find(); iterate();                                                                                                                                                                        Customer customer = session.get(Customer.class,1L); 
  2. 状态转换      持久==》瞬时   delete();                                                                                                                                                                               持久==》脱管   session.close();   session.clear();  session.evict(Object obj);
  • 脱管态对象
  1. 获得  Customer customer = new Customer();                                                                                                                                                     customer.setCust_id(1L);这样可以将customer看作一个脱管态对象 
  2. 状态转换  脱管==》持久   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的工具类
 * @author jt
 *
 */
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();

猜你喜欢

转载自blog.csdn.net/bright_light12345/article/details/83242802