一、主键生成策略
(1)数据库的主键:
能够区分表里面每一行数据,非空且唯一
(2)主键的分类:
自然主键 :具有实际意义的列来作为主键
代理主键 :没有实际意义的列来作为主键 --单体项目
(3)Hibernate的主键生成策略分为两类:
一类是JPA标准的主键生成策略:auto策略、table策略、sequence策略、identity策略;
一类是Hibernate框架特有的主键生成策略。
(4)详解JPA标准的主键生成策略:
①AUTO自动策略(最常用,默认的配置)
假如数据库是Oracle,则选择Sequence,
数据库是MySQL,则选择Identity。
如果不特别指定,这是默认的主键生成策略:
@GeneratedValue(strategy = GenerationType.AUTO) //auto的方式,括号里面的配置可以默认不写
private Long id;
②IDENTITY自增策略
mysql采用这种策略
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
③SEQUENCE序列策略
假如数据库是Oracle,则选择Sequence
MySQL不支持SEQUENCE.
SEQUENCE序列策略使用方式:
第一步:修改配置文件persistence.xml
<property name="hibernate.connection.driver_class" value="oracle.jdbc.driver.OracleDriver" />
<property name="hibernate.connection.url" value="jdbc:oracle:thin:@localhost:1521:ORCL" />
<property name="hibernate.connection.username" value="itsource" />
<property name="hibernate.connection.password" value="itsource" />
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
第二步:
name:序列生成器的名称
sequenceName:oracle数据库中的序列生成器名称
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ")
@SequenceGenerator(name = "SEQ", sequenceName = "OIDDOMAIN_SEQUENCE")
private Long id;
上述声明等同于在数据库上创建一个名称为OIDDOMAIN_SEQUENCE的序列
如果不指定序列生成器的名称sequenceName = "OIDDOMAIN_SEQUENCE",
则使用厂商提供的默认序列生成器,比如Hibernate默认提供的序列名称为HIBERNATE_SEQUENCE。
④TABLE 表的生成策略(额外创建一张表来维护主键)
用法:
@TableGenerator(name = "SEQ", table = "OidDomain_TABLE", pkColumnName = "SEQUENCE_NAME", valueColumnName = "SEQUENCE_COUNT", initialValue = 1, allocationSize = 1)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "SEQ")
private Long id;
这种方式通用性最好,所有的关系型数据库都支持,但是不能充分利用具体数据库的特性,性能差,建议不要优先使用。
二、JPA持久对象的状态
持久对象的状态:一个实体交给jpa维护的时候,这个实体在不同的时期,状态不一样
1.临时状态(transient):瞬时状态
刚刚用new语句创建,没有和entityManager发生关系
没有被持久化,不处于entityManager中。该对象成为临时对象
2.持久化状态(persistent):托管状态
和entityManager发生关系.已经被持久化.
3.游离状态(detached):脱管状态
脱离entityManager的管理,已经被持久化,不存在的entityManager里面
4.删除状态(removed):从JPA才开始有的状态
只有调用了entityManager.remove(domain对象)方法
对象有关联的ID,并且在entityManager管理下,
但是已经被计划删除,事务提交就真的被删除了。
代码体现:
@Test
public void save() throws Exception {
StateDomain stateDomain = new StateDomain();// 临时状态
stateDomain.setName("itsource");// 临时状态
EntityManager entityManager = JPAUtils.getEntityManager();
entityManager.getTransaction().begin();
entityManager.persist(stateDomain);// 持久状态
entityManager.getTransaction().commit();
entityManager.close();// 游离状态
}
三、JPA使用注意事项
1.脏数据更新
一个持久化状态数据,修改非主键的值,会在commit提交的时候,自动会发送sql去更新
2.identifier of an instance of 问题
持久状态的对象是不能修改OID(不能修改主键)
持久层修改id会报下面的错误,大家要注意小心:
org.hibernate.HibernateException: identifier of an instance of cn.itsource.jpa.state.StateDomain was altered from 1 to 200
3.持久对象(domain层)定义规则
(1)类不能定义为final类
因为domain类需要被继承的,否则延迟加载会失效
(2)所有属性的类型都必须是包装类型
不能是8个基本类型(int,byte,short,long,char,boolean,float,double)
因为JPA内部代码很多判断都是基于是否等于null
(3)必须有默认无参构造方法
因为find方法获取的时候会在内存实例化domain对象
否则报org.hibernate.InstantiationException: No default constructor for entity异常
四、域对象之间的关系(domain对象)
1.域对象之间的关系
(1)依赖关系
JavaBean之间的依赖关系
分层:表现层,业务层,持久层(依赖关系)
依赖:一般指controll,service,dao层之间的关系
(2)关联关系
关联按照多重性可分为:一对一、一对多、多对一和多对多。
按照导航性可分为:单向关联和双向关联。
(3)聚合关系
(本质还是双向多对一,一对多)
表示整体与部分的关系,整体和部分可以分开单独存在。
电脑(主板,CPU,IO,内存条)
(4)组合关系
(本质还是聚合关系,双向多对一,一对多)
强聚合关系,整体和部分之间不能分开。
如:人(头,手),订单模型,超市购物购物清单
2.单向多对一
如:多张表对应统一表的数据
@Entity
@Table(name = "t_product")
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY) //多对一 ,默认位EAGER
@JoinColumn(name = "dir_id") //设置外键名称,不配置默认一方属性名_id
private ProductDir dir;
public void testManytoOne() throws Exception{
Product product1 = new Product();
product1.setName("刘星");
Product product2 = new Product();
product2.setName("波波");
ProductDir productDir = new ProductDir();
productDir.setName("人");
product1.setDir(productDir);
product2.setDir(productDir);
EntityManager entityManager = JpaUtil.getEntityManager();
entityManager.getTransaction().begin();
//先保存分类 在保存产品 -- 3条 -- 正确保存方式
//先保存产品 在保存分类 -- 5条 -- 在保存产品的时候,还没有分类 当分类增加之后,会额外发送sql语句更新
//所以,先保存一方,再保存多方,性能会更高
entityManager.persist(productDir);
entityManager.persist(product1);
entityManager.persist(product2);
entityManager.getTransaction().commit();
entityManager.close();
}
3.fetch抓取策略
抓取策略就是告诉JPA怎样发出sql获取关联对象
(1)FetchType.EAGER:fetch的默认值,立即加载
@ManyToOne(fetch = FetchType.EAGER) 表示迫切加载 -- 不使用都把数据加载出来 发送左外连接查询数据
(2)FetchType.LAZY:延迟加载
@ManyToOne(fetch = FetchType.LAZY ) 表示懒加载 -- 需要使用的时候,才发送sql查询
@Entity
@Table(name = "t_product")
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY) //多对一 ,默认位EAGER
@JoinColumn(name = "dir_id") //设置外键名称,不配置默认一方属性名_id
private ProductDir dir;
五、缓存
1.一级缓存:不需要做任何配置,默认提供
一级缓存命中条件:同一个entityManagerFactory,同一个entityManager,OID相同
2.二级缓存:需要配置
(1)Pom.xml添加二级缓存jar文件
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>4.3.8.Final</version>
</dependency>
(2)添加persistence.xml配置信息
<!-- 启用二级缓存 -->
<property name="hibernate.cache.use_second_level_cache" value="true" />
<!-- 二级缓存的实现类,文档里面的包名有错的 -->
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory" />
<!-- 启用查询缓存 -->
<property name="hibernate.cache.use_query_cache" value="true" />
(3)在上面添加配置二级缓存扫描的策略
<!-- ALL:所有的实体类都被缓存 -->
<!-- NONE:所有的实体类都不被缓存. -->
<!-- ENABLE_SELECTIVE:标识 @Cacheable(true) 注解的实体类将被缓存 -->
<!-- DISABLE_SELECTIVE:缓存除标识 @Cacheable(false) 以外的所有实体类 -->
<!-- UNSPECIFIED:默认值,JPA 产品默认值将被使用 -->
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
(4)domain类的二级缓存
二级缓存类配置:命中条件:同一个EntityManagerFactory,不同的entityManager,OID相同
@Entity
@Table(name = "t_product")
@Cacheable(true) //默认位true
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
//测试二级缓存
@Test
public void testCache2() throws Exception{
//以下命中后,只会查询一次
EntityManager entityManager1 = JpaUtil.getEntityManager();
entityManager1.find(Product.class, 1L);
EntityManager entityManager2 = JpaUtil.getEntityManager();
entityManager2.find(Product.class, 1L);
//注意:关流要放在后面,否则二级缓存不能命中
entityManager1.close();
entityManager2.close();
}
3.查询缓存:依赖于二级缓存
命中条件:同一个EntityManagerFactory,不同一个entityManager,发出的sql语句必须相同并且查询的条件值也要相同
// 1.如果查询缓存有查询条件,就不要使用查询缓存,因为命中率非常低
// 2.查询缓存里面查询指的是使用jpql,sql进行的查询,就是调用了createQuery方法,find方法不是走查询缓存
// 3.进行domain类的二级缓存的配置
//4.需要将查询结果放进缓存
@Entity
@Table(name = "t_product")
@Cacheable(true) //默认位true
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
@Test
public void testCache3() throws Exception{
EntityManager entityManager1 = JpaUtil.getEntityManager();
String jpql = "select t from Product t where id =?";
Query query1 = entityManager1.createQuery(jpql).setParameter(1, 1L);
//把查询结果放入缓存里面
query1.setHint(QueryHints.HINT_CACHEABLE, true);
System.out.println(query1.getResultList().size());
EntityManager entityManager2 = JpaUtil.getEntityManager();
String jpq2 = "select t from Product t where id =?";
Query query2 = entityManager2.createQuery(jpq2).setParameter(1, 1L);
//把查询结果放入缓存里面
query2.setHint(QueryHints.HINT_CACHEABLE, true);
System.out.println(query2.getResultList().size());
}
4.缓存理论部分
面试题:二级缓存使用场景?
1.读取大于修改;
2.对数据要有独享控制,数据不会被第三方修改;
3.可以容忍出现无效数据,非关键数据(不是财务数据等)
4.数据量不能超过内存容量,数据量特别巨大,此时不适合于二级缓存(钝化可以解决)
底层框架ehcache支持钝化,其配置可以看ehcache.xml里面的配置,即下面第5点
5.可选ehcache.xml
了解网站:http://www.mamicode.com/info-detail-1291774.html
默认这个缓存配置文件,位置如下.
里面配置,及属性含义如下:
若要自行配置,则先拷贝,再修改.
maxElementsInMemory="10000" //内存中最大缓存对象数
maxElementsOnDisk="10000000" //硬盘中最大缓存对象数,若是0表示无穷大
eternal="false" //true表示对象永不过期,默认位false,一但设置了,timeout将不起作用。
timeToIdleSeconds="120" //设定允许对象处于空闲状态的最长时间,以秒为单位。
timeToLiveSeconds="120" //设定对象允许存在于缓存中的最长时间,以秒为单位。
overflowToDisk="true" //true 表示当内存缓存的对象数目达到了maxElementsInMemory界限后,会把溢出的对象写到硬盘缓存中。
diskPersistent="false" //是否缓存虚拟机重启期数据
diskExpiryThreadIntervalSeconds="120" //磁盘失效线程运行时间间隔,默认为120秒
memoryStoreEvictionPolicy="LRU" //当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。可选策略有:LRU(最近最少使用,时间位参考点)、LFU(最不经常使用 -->次数为参考点)、 FIFO(使用最多先进先出)
6.缓存命中条件
(1)一级缓存
同一个EntityManagerFactory,同一个EntityManager,OID相同
(2)Domain类的二级缓存配置
同一个EntityManagerFactory,不同一个EntityManager,OID相同
需要配置
(3)查询缓存
同一个EntityManagerFactory,不同一个EntityManager,发出的sql语句必须相同并且查询的条件值也要相同
需要二级缓存的配置