Jpa2

一、主键生成策略

(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语句必须相同并且查询的条件值也要相同
需要二级缓存的配置
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值