package cn.jobs1127.jpa.crud;
import java.util.Date;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class entityManager_CRUD_Test {
private EntityManagerFactory entityManagerFactory;
private EntityManager entityManager;
private EntityManager entityManager1;
@Before
public void init(){
/**
* createEntityManager():用于创建实体管理器对象实例。
* createEntityManager(Map map):用于创建实体管理器对象实例的重载方法,Map 参数用于提供 EntityManager 的属性。
*/
entityManagerFactory = Persistence.createEntityManagerFactory(“jpa-jobs1127”);
entityManager = entityManagerFactory.createEntityManager();
entityManager1 = entityManagerFactory.createEntityManager();
}
@After
public void destroy(){
entityManager.close();
entityManagerFactory.close();
}
/**
* 类似于 hibernate 的 save 方法. 使对象由临时状态变为持久化状态.
* 和 hibernate 的 save 方法的不同之处: 若对象有 id, 则不能执行 insert操作, 而会抛出异常.
*/
@Test
public void testPersistence(){
System.out.println(entityManager1==entityManager);
Customer customer = new Customer();
customer.setAge(15);
customer.setBirth(new Date());
customer.setCreatedTime(new Date());
customer.setEmail("bb@163.com");
customer.setLastName("BB");
//customer.setId(2);//有id,不执行,hibernate的session save时可以
/**
* 不开启事务是不会执行持久化操作的,
* 可以猜测这个entityManager对象类似Hibernate的getCurrentSession()的session对象
*/
entityManager.getTransaction().begin();
entityManager.persist(customer);
entityManager.getTransaction().commit();
System.out.println(customer.getId());
}
@Test
public void testRemove1(){
// Customer customer = new Customer();
// customer.setId(2);//游离对象不可移除,会出错
//find类似于Hibernate的get()方法,找到的是持久化状态的对象
Customer customer = entityManager.find(Customer.class, 2);
//没开启事务是操作不成功的
entityManager.getTransaction().begin();
entityManager.remove(customer);
entityManager.getTransaction().commit();
}
@Test
public void testRemove2(){
// Customer customer = new Customer();
// customer.setId(2);//游离对象不可移除,会出错
//getReference()类似于Hibernate的load()方法,拿到的是动态代理对象,处于detached状态
Customer customer = entityManager.getReference(Customer.class, 3);
//没开启事务是操作不成功的
entityManager.getTransaction().begin();
entityManager.remove(customer);
entityManager.getTransaction().commit();
}
@Test
public void testMerge3(){
Customer customer = new Customer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreatedTime(new Date());
customer.setEmail("dd@163.com");
customer.setLastName("DD");
customer.setId(4);
Customer customer2 = entityManager.find(Customer.class, 4);
entityManager.merge(customer);
//customer是游离状态的 ,customer2是持久化状态的,存放的位置是不一样的,customer2存放再缓存里了。
System.out.println(customer == customer2); //false
}
//若传入的是一个游离对象, 即传入的对象有 OID.
//1. 若在 EntityManager 缓存中没有该对象
//2. 若在数据库中也有对应的记录
//3. JPA 会查询对应的记录, 然后返回该记录对一个的对象, 再然后会把游离对象的属性复制到查询到的对象中.
//4. 对查询到的对象执行 update 操作.
@Test
public void testMerge2(){
Customer customer = new Customer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreatedTime(new Date());
customer.setEmail("ee@163.com");
customer.setLastName("EE");
customer.setId(4);
entityManager.getTransaction().begin();
Customer customer2 = entityManager.merge(customer);
entityManager.getTransaction().commit();
//customer是游离状态的 ,customer2是持久化状态的
System.out.println(customer == customer2); //false
}
//若传入的是一个游离对象, 即传入的对象有 ID.
//1. 若在 EntityManager 缓存中没有该对象
//2. 若在数据库中也没有对应的记录
//3. JPA 会创建一个新的对象, 然后把当前游离对象的属性复制到新创建的对象中
//4. 对新创建的对象执行 insert 操作.
// 但如果数据库中有记录,则执行修改操作
/**
* 总的来说: 类似于 hibernate Session 的 saveOrUpdate 方法.
*
* 对象没有id,执行插入操作save
* 对象有id,且和数据库中有对应的id,修改操作update
* 对象有id,但数据库中找不到对应的id,则抛弃id,进行插入操作save
*/
//1. 若传入的是一个临时对象
// 会创建一个新的对象, 把临时对象的属性复制到新的对象中, 然后对新的对象执行持久化操作. 所以
// 新的对象中有 id, 但以前的临时对象中没有 id.
@Test
public void testMerge1(){
Customer customer = new Customer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreatedTime(new Date());
customer.setEmail("cc@163.com");
customer.setLastName("CCccc");
customer.setId(5);//控制是否有id
entityManager.getTransaction().begin();
Customer customer2 = entityManager.merge(customer);
entityManager.getTransaction().commit();
System.out.println("customer#id:" + customer.getId());
System.out.println("customer2#id:" + customer2.getId());
}
//类似于 hibernate 中 Session 的 load() 方法,懒加载,用到时才查询
@Test
public void testGetReference(){
/**
* getReference获取到的是动态代理对象
*/
Customer customer = entityManager.getReference(Customer.class, 1);
System.out.println(customer.getClass().getName());
System.out.println("-------------------------------------");
//实际使用时才去发出SQL语句去数据库拿取
System.out.println(customer);
}
//类似于 hibernate 中 Session 的 get() 方法.
@Test
public void testFind() {
//entityManager.find(Class<T> arg0, Object arg1);
Customer customer = entityManager.find(Customer.class, 1);
System.out.println("-------------------------------------");
System.out.println(customer);
}
/**
* 类似于 hibernate 中 Session 的 load() 方法,懒加载,用到时才查询.
* 拿取相同数据,jpa的entityManager对象和Hibernate的session对象相似,也有session缓存
*/
@Test
public void testGetReference_cache1(){//测试jpa的一级缓存
/**
* getReference获取到的是动态代理对象
*/
Customer customer = entityManager.getReference(Customer.class, 1);
System.out.println("-------------------------------------");
//实际使用时才去发出SQL语句去数据库拿取
System.out.println(customer);
Customer customer2 = entityManager.getReference(Customer.class, 1);
System.out.println(customer2);
//不同的entityManager具有不同的session缓存,和Hibernate的session一样
Customer customer3 = entityManager1.getReference(Customer.class, 1);
System.out.println(customer3);
Customer customer4 = entityManager1.getReference(Customer.class, 1);
System.out.println(customer4);
}
/**
* 类似于 hibernate 中 Session 的 load() 方法,懒加载,用到时才查询.
* 拿取相同数据,jpa的entityManager对象和Hibernate的session对象相似,也有session缓存.
* 配置二级缓存,进行测试
*/
/**
* 设置二级缓存的步骤:
* 1、在hibernate.cfg.xml配置文件里设置属性:true声明要使用二级缓存;
* 2、在hibernate.cfg.xml配置文件里设置属性:org.hibernate.cache.EhCacheProvider声明要使用哪个cache提供商的cache;即对象二级缓存的实现;
* 3、由于用的是EHCacheProvider,所有要提供相关的jar文件:ehcache1.2.3.jar,commons-logging-1.1.1.jar,以及相关的配置文件ehcache.xml,在hibernate的ect目录下;
* 4、针对具体的类上通过@Cache注解使用二级缓存(让该类使用配置好的二级缓存),在Msg类上加注解@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
*
* 在配置好缓存的cache.provider_class,并导入了相关的支持jar包,就可以配置查询缓存了。
* 查询缓存(query_cache);
* 查询缓存的配置和使用也是很简单的:
* 1>查询缓存的启用不但要在配置文件中进行配置
* true
* 2>还要在程序中显示的进行启用
* query.setCacheable(true);
* Query如果被赋予了利用查询缓存的能力,那么这时list从数据库里拿取来的数据会存放到sessionFactory缓存里,
* 在以后拿取数据的时候也会去二级缓存里拿取了(不具备此能力的时候是不会利用缓存拿取数据的)
*/
@Test
public void testGetReference_cache2(){//测试jpa的二级缓存
/**
* getReference获取到的是动态代理对象
*/
Customer customer = entityManager.getReference(Customer.class, 1);
System.out.println(“————————————-“);
//实际使用时才去发出SQL语句去数据库拿取
System.out.println(customer);
Customer customer2 = entityManager.getReference(Customer.class, 1);
System.out.println(customer2);
//不同的entityManager具有不同的session缓存,和Hibernate的session一样
Customer customer3 = entityManager1.getReference(Customer.class, 1);
System.out.println(customer3);
Customer customer4 = entityManager1.getReference(Customer.class, 1);
System.out.println(customer4);
}
//若传入的是一个游离对象, 即传入的对象有 OID.
//1. 若在 EntityManager 缓存中有对应的对象
//2. JPA 会把游离对象的属性复制到查询到EntityManager 缓存中的对象中.
//3. EntityManager 缓存中的对象执行 UPDATE.
//类似于 hibernate 中 Session 的 delete 方法. 把对象对应的记录从数据库中移除
//但注意: 该方法只能移除 持久化 对象. 而 hibernate 的 delete 方法实际上还可以移除 游离对象.
/*
* 缓存是介于应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的频次,
* 从而提高了应用的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,
* 在特定的时刻或事件会同步缓存和物理数据源的数据。
*
* 缓存的介质一般是内存,所以读写速度很快。但如果缓存中存放的数据量非常大时,也会用硬盘作为缓存介质。
* 缓存的实现不仅仅要考虑存储的介质,还要考虑到管理缓存的并发访问和缓存数据的生命周期。
*
* Hibernate的缓存包括Session的缓存和SessionFactory的缓存,
* 其中SessionFactory的缓存又可以分为两类:内置缓存和外置缓存。
* Session的缓存是内置的,不能被卸载,也被称为Hibernate的第一级缓存。
*
* SessionFactory的内置缓存和Session的缓存在实现方式上比较相似,
* 前者是SessionFactory对象的一些集合属性包含的数据,后者是指Session的一些集合属性包含的数据。
* SessionFactory的内置缓存中存放了映射元数据和预定义SQL语句,
* 映射元数据是映射文件中数据的拷贝,而预定义SQL语句是在Hibernate初始化阶段根据映射元数据推导出来,
* SessionFactory的内置缓存是只读的,应用程序不能修改缓存中的映射元数据和预定义SQL语句,
* 因此SessionFactory不需要进行内置缓存与映射文件的同步。
*
* SessionFactory的外置缓存是一个可配置的插件。在默认情况下,SessionFactory不会启用这个插件。
* 外置缓存的数据是数据库数据的拷贝,外置缓存的介质可以是内存或者硬盘。
* SessionFactory的外置缓存也被称为Hibernate的第二级缓存。
* Hibernate的这两级缓存都位于持久化层,存放的都是数据库数据的拷贝
* ———————————————————————-
* 持久化层的缓存的范围,缓存的范围分为三类:
* 1、事务范围:缓存只能被当前事务访问。类似于session缓存(一级缓存)
* 缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。
* 在此范围下,缓存的介质是内存。事务可以是数据库事务或者应用事务,每个事务都有独自的缓存,
* 缓存内的数据通常采用相互关联的的对象形式。
*
* 2、进程范围:缓存被进程内的所有事务共享。类似于sessionFactory缓存(二级缓存)
* 这些事务有可能是并发访问缓存,因此必须对缓存采取必要的事务隔离机制。
* 缓存的生命周期依赖于进程的生命周期,进程结束时,缓存也就结束了生命周期。
* 进程范围的缓存可能会存放大量的数据,所以存放的介质可以是内存或硬盘。
* 缓存内的数据既可以是相互关联的对象形式也可以是对象的松散数据形式。
* 松散的对象数据形式有点类似于对象的序列化数据,但是对象分解为松散的算法比对象序列化的算法要求更快。
*
* 3、集群范围:在集群环境中,缓存被一个机器或者多个机器的进程共享。
* 缓存中的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据的一致性,
* 缓存中的数据通常采用对象的松散数据形式。(对大多数应用来说,应该慎重地考虑是否需要使用集群范围的缓存,
* 因为访问的速度不一定会比直接访问数据库数据的速度快多少。)
*
* 持久化层可以提供多种范围的缓存。如果在事务范围的缓存中没有查到相应的数据,
* 还可以到进程范围或集群范围的缓存内查询,如果还是没有查到,那么只有到数据库中查询。
* 事务范围的缓存是持久化层的第一级缓存,通常它是必需的;
* 进程范围或集群范围的缓存是持久化层的第二级缓存,通常是可选的。
*
* ———————————————————————-
* 持久化层的缓存的并发访问策略:
* 当多个并发的事务同时访问持久化层的缓存的相同数据时,会引起并发问题,必须采用必要的事务隔离措施。
* 在进程范围或集群范围的缓存,即第二级缓存,会出现并发问题。
* 因此可以设定以下四种类型的并发访问策略,每一种策略对应一种事务隔离级别。
*
* 事务型:仅仅在受管理环境中适用。它提供了Repeatable Read事务隔离级别。
* 对于经常被读但很少修改的数据,可以采用这种隔离类型,因为它可以防止脏读和不可重复读这类的并发问题。
*
* 读写型:提供了Read Committed事务隔离级别。仅仅在非集群的环境中适用。
* 对于经常被读但很少修改的数据,可以采用这种隔离类型,因为它可以防止脏读这类的并发问题。
*
* 非严格读写型:不保证缓存与数据库中数据的一致性。
* 如果存在两个事务同时访问缓存中相同数据的可能,必须为该数据配置一个很短的数据过期时间,从而尽量避免脏读。
* 对于极少被修改,并且允许偶尔脏读的数据,可以采用这种并发访问策略。
*
* 只读型:对于从来不会修改的数据,如参考数据,可以使用这种并发访问策略。
*
* 事务型并发访问策略是事务隔离级别最高,只读型的隔离级别最低。事务隔离级别越高,并发性能就越低。
*
* 什么样的数据适合存放到第二级缓存中?
1、经常被读,很少被修改的数据
2、不是很重要的数据,允许出现偶尔并发的数据
3、不会被并发访问的数据
*
* 不适合存放到第二级缓存的数据?
1、经常被修改的数据
2、财务数据,绝对不允许出现并发
3、与其他应用共享的数据。
*
* Hibernate提供了两级缓存,第一级是Session的缓存。
* 由于Session对象的生命周期通常对应一个数据库事务或者一个应用事务,因此它的缓存是事务范围的缓存。
* 第一级缓存是必需的,不允许而且事实上也无法比卸除。
*
* 第二级缓存是一个可插拔的的缓存插件,它是由SessionFactory负责管理。
* 由于SessionFactory对象的生命周期和应用程序的整个过程对应,
* 因此第二级缓存是进程范围或者集群范围的缓存。这个缓存中存放的对象的松散数据。
* 第二级对象有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。
* 缓存适配器用于把具体的缓存实现软件与Hibernate集成。
* 第二级缓存是可选的,可以在每个类或每个集合的粒度上配置第二级缓存。
*
* Hibernate的二级缓存策略的一般过程如下:
* 1) 条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。
* 2) 把获得的所有数据对象根据ID放入到第二级缓存中。
* 3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,
* 那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。
* 4) 删除、更新、增加数据的时候,同时更新缓存。
*
*
*
*
*
*
*
*
*/
}