自然主键和代理主键
关系数据库主要依靠主键区分不同的记录,主键又有自然主键和代理主键之分。
- 自然主键
自然主键:就是充当主键的字段本身具有一定的含义,是构成记录的组成部分,比如学生的身份证号,除了充当主键之外,同时也是学生记录的重要组成部分。
- 代理主键
代理主键:就是充当主键的字段本身不具有业务意义,只具有主键作用,比如自动增长的ID。
主键生成策略
- native
该生成主键生成策略是自增长,但是该生成策略会根据数据库的不同,来调用各数据库底层的自增长的生成策略.
- UUID
适用于字符串类型主键,使用hibernate中的随机方式生成字符串主键
- identity
适用于short,int,long,类型的主键,由于采用的是hibernate的自动增长机制,所只能在单线程中使用,其内部实现过程是,首先发送一条语句,select max(id) form 表,然后将id+1作为插入数据的主键,如果在多线程中使用,有可能会出现重复的主键值,这是不允许的。
- sequence
自增长类型,采用序列方式(适用于Oracle数据库)
- increment
适用于short,int,long类型的主键,适用的是数据库底层的自动增长机制。适用于有自动增长机制的数据库(mysql、mssql)
- assigned
hibernate放弃主键的管理,需要通过手动编写程序或者用户自己设置
持久化
持久化的划分
- 瞬时态
瞬时态的对象是由 new 关键字开辟内存空间的对象,不存在持久化标识 OID(相当于主键值),且未与任何的 Session 实例相关联,在数据库中也没有记录,失去引用后将被 JVM 回收。瞬时对象在内存孤立存在,它是携带信息的载体,不和数据库的数据有任何关联关系。
- 持久态
持久态的对象存在一个持久化标识 OID,当对象加入到 Session 缓存中时,就与 Session 实例相关联。它在数据库中存在与之对应的记录,每条记录只对应唯一的持久化对象。需要注意的是,持久态对象是在事务还未提交前变成持久态的。
当直接执行 Session 的 get()、load()、find() 或 iterate() 等方法从数据库中查询出对象时,查询到的对象也会处于持久态。
如创建对象,并执行sessio.save(对象);该对象就会变成持久层对象,在Java中修改持久化对象的属性值,数据库会自动更新为更改后的值
- 脱管态\离线态
脱管态也称离线态或者游离态,当持久化对象与 Session 断开时就变成了脱管态,但是脱管态依然存在持久化标识 OID,只是失去了与当前 Session 的关联。需要注意的是,脱管态对象发生改变时 Hibernate 是不能检测到的。
session对象执行close\evict\clear操作时,对象会进入脱管态。
如session执行close方法关闭资源时,对象和session失去关联,该对象就变成托管态\离线态
持久化对象状态的转换
持久化转化方法缓存
缓存是一种优化方式,将数据存放在内存中,使用的时候直接从缓存中获取
不用从数据库中获取
一级缓存
session级别的缓存
一级缓存是由Session中Java集合构成的
- 当执行Session中save、update、saveOrUpdate方法
- 如果Session缓存中没有该对象,则自动的从数据库查询相对应数据,查询数据并写入缓存中
- 当执行load、get方法,以及Query接口中的list、iterator方法时,会判断缓存中是否存在该对象,有则返回,没有则从数据库中查询,并写入缓存中,并返回。
- 当调用close、clear方法时,缓存会被清除
public class HibernateTest{
@Test
public void hibernateCache () {
// 获取Session对象
Session session = HibernateUtils.getSession();
// 开启事务
Transaction transaction = session.beginTransaction();
// 第一次查询
Customer customer = session.get(Customer.class, 2L);
// 第一次打印,缓存已经写入
System.out.println(customer);
// 第二次查询
Customer customer1 = session.get(Customer.class, 2L);
// 第二次打印,从缓存中取出数据
System.out.println(customer1);
// 事务提交
transaction.commit();
// 释放资源
session.close();
}
}
/**
只执行了一条SQL语句,第二次查询的结果是从缓存中取出的
Hibernate:
select
customer0_.cust_id as cust_id1_0_0_,
customer0_.cust_name as cust_nam2_0_0_,
customer0_.cust_source as cust_sou3_0_0_,
customer0_.cust_industry as cust_ind4_0_0_,
customer0_.cust_level as cust_lev5_0_0_,
customer0_.cust_phone as cust_pho6_0_0_,
customer0_.cust_mobile as cust_mob7_0_0_
from
customer customer0_
where
customer0_.cust_id=?
Customer(cust_id=2, cust_name=我, cust_source=null, cust_industry=null, cust_level=null, cust_phone=null, cust_mobile=null)
Customer(cust_id=2, cust_name=我, cust_source=null, cust_industry=null, cust_level=null, cust_phone=null, cust_mobile=null)
*/
二级缓存
二级缓存是SessionFactory级别的缓存
现在自己去配置,默认是关闭的,现在企业都不会使用,现在大多使用redis
缓存和快照区域
当实体对象变成持久态对象的时候,和数据库表关联后。在session中会保存两份数据的副本。
一份是缓存,一个是快照。
缓存的作用:用于提高查询的效率
快照的作用:用于更新数据,作对比使用。
- 使用id进行查询数据库,将查询的结果放置到session一级缓存中,同时复制一份放入session快照中
- 当使用commit的时候,同时清理session的一级缓存(flush)
- 当清理session中缓存时,会使用UID判断一级缓存中的对象和快照的对象进行比对
- 如快照和缓存中的对象中的属性值发生变化,则会执行update语句,此时更新数据库,更新成一级缓存中的数据
- 如快照和缓存中的对象中的属性值无变化,则不执行update语句
目的: 确保和数据库中的数据保持一致
这就是持久层对象为什么更新属性值,数据库的值为什么更新为持久层对象的属性值的原因
注意: 在一个事务中,如果为某持久化对象set了新的值,那么在提交事务时,Hibernate就会去比较你哪些持久化对象发生了变化,如果找到了,就会自动更新到数据库中。(注意,如果在事务外做的操作,Hibernate是不会帮你更新的)
事务
事务: 事务是逻辑上的一组操作,要么全部成功.要么全部失败
事务的特性
- 原子性
事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体. - 隔离性
同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。 - 持久性
事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。 - 一致性
事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
事务的隔离级别
事务隔离级别Hibernate 设置事务隔离级别
在核心配置文件 hibernat.cfg.xml
当中,通过数字代表不同隔离级别
<property name="hibernate.connection.isolation">4</property>
hibernate事务的处理
为什么要在业务层使用事务?
在业务层使用事务时,必须得要保证获取事物的连接和dao层操作的连接是同一个,否则就管理了不对应的操作
TIM截图20191117121353.png
-
使用jdbc当中事务业务层处理方法
1.1. 向下传递就开始在业务层先创建好一个连接,传给dao层,让dao层使用这个连接执行操作
1.2 使用ThreadLocal对象
在service方法当中把创建的连接绑定到对应的threadLocal当中
在dao方法当中,通过当前的线程获得连接对象 -
hibernate当中处理方法
2.1. Hibernate框架,内部已经绑定好了ThreadLocal
2.2. 在SessionFactory中,提供了一个方法,getCurrentSession()方法
2.3. 获取当前线程中的session,此方法默认不能用
2.4. 通过配置完成
```xml <!-- 在核心配置文件当中配置 --> <property name="current_session_context_class">thread</property> <!-- 创建一个session绑定到当前线程通过它来操作时, 不需要 close,执行结束后,会自动的close() --> ```
2.5. 测试代码
<!--pom文件中导入的jar包 --> <dependencies> <!-- 测试Jar包 Junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!--Hibernate的Jar包--> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.3.13.Final</version> </dependency> <!--连接池和mysql连接jar--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.45</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.20</version> </dependency> <!-- Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> <scope>provided</scope> </dependency> <!--log4j日志jar--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 设置连接数据库的url和用户名和密码和数据库驱动--> <property name="url">jdbc:mysql:///hibernate</property> <property name="username">root</property> <property name="password">123456</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <!-- 打印SQL语句 --> <property name="hibernate.show_sql">true</property> <!-- 格式化SQL --> <property name="hibernate.format_sql">true</property> <!-- 设置hibernate的方言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property> <!-- 设置自动创建表 --> <property name="hibernate.hbm2ddl.auto">update</property> <!--阿里巴巴 Druid 连接池 注意:如果使用Druid连接池的话需要把数据基本连接属性改为Druid的属性--> <property name="hibernate.connection.provider_class"> com.alibaba.druid.support.hibernate.DruidConnectionProvider </property> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize">1</property> <property name="minIdle">5</property> <property name="maxActive">20</property> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait">60000</property> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis">60000</property> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis">300000</property> <!-- 设置事务隔离级别 --> <property name="hibernate.connection.isolation">4</property> <!-- 创建一个session绑定到当前线程 --> <property name="current_session_context_class">thread</property> <!-- 加载映射(mapper)文件 --> <mapping resource="mapper/customer.hbm.xml"></mapping> </session-factory> </hibernate-configuration>
public class HibernateUtils { public static final SessionFactory sessionFactory; static { // 读取配置,并创建工厂类SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); } /** * 获取Session对象 * @return Session */ public static Session getSession () { return sessionFactory.openSession(); } /** * 获取Session对象,不过是从内部已经绑定好了ThreadLocal,获取的Session对象 * @return Session */ public static Session getCurrentSession(){ return sessionFactory.getCurrentSession(); } }
public class HibernateTest{ @Test public void hibernateTransaction () { // 获取Session对象 Session session = HibernateUtils.getCurrentSession(); // 开启事务 Transaction transaction = session.beginTransaction(); // 只要有一个对象执行失败,该事务就会回滚,要么全部成功,要么全部失败 // 删除操作 Customer customer1 = new Customer(); customer1.setCust_id(2L); session.delete(customer1); // 插入操作 Customer customer = new Customer(); customer.setCust_name("王五"); session.save(customer); transaction.commit(); // 在配置文件设置了 } }
网友评论