jdbc技术:
JPA是什么
Java Persistence Api:用于对象持久化的api
Java EE 5.0平台标准的ORM框架,使得应用程序以统一的方式访问持久层
JPA和hibernate的关系
JPA是hibernate的一个抽象(就像JDBC和JDBC驱动一样)
JPA是规范:JPA本质上是一种ORM规范,不是ORM框架(因为JPA未提供ORM实现,它只是提供一些规范,具体实现有ORM厂商提供)
hibernate是实现:hibernate除了是ORM框架之外,还是一种JPA实行
从功能上来说,JPA是hibernate的一个功能子集
JPA的提供商
JPA的目标之一是制定一个由很多供应商的e实现的API,hibernate、OpenJPA、TopLink
JPA的优势
标准化:提供相同的API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA实现框架下进行
简单易用、集成方便:JPA的主要目标之一就是提供更加简单的编程模型,在JPA框架下创建实体和创建Java类一样简单,只需使用注解即可
执行速度可媲美JDBC的查询能力:JPA的查询语言是面向对象的,支持批量修改,join,group by、having等通常只有SQL才提供的高级查询语言,甚至还支持子查询
支持面向对象的高级查询:JPA中能够支持面向对象的高级特性,如类之间的继承,多态和类之间的复杂关系,最大限度的支持面向对象
JPA包括三方面的内容:
ORM映射元数据:JPA支持XML和JDK 5.0注解两种数据形式,元数据描述对象和表之间的关系,框架据此将实体对象持久化到数据库中
JPA的API:用来操作实体对象,执行CURD对象,框架在后台完成的所有事情,开发者从繁琐的JDBC和SQL中解放出来
查询语言(JPQL):通过面向对象而非面向数据库的查询语言,避免程序和具体的SQL紧密耦合
使用JPA持久化对象的步骤
1、创建persistence.xml文件,在这个配置文件配置持久化操作
指定跟哪个数据库进行交互
需要指定JPA使用哪个框架以及配置该框架的基本属性
2、创建实体类
使用注解来描述实体类和数据库之间的关系
3、使用JPA API完成数据的增删查改
入门程序:
persistence.xml
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="jpa" transaction-type="RESOURCE_LOCAL"> <!-- 配置使用什么 ORM 产品来作为 JPA 的实现 1. 实际上配置的是 javax.persistence.spi.PersistenceProvider 接口的实现类 2. 若 JPA 项目中只有一个 JPA 的实现产品, 则也可以不配置该节点. --> <provider>org.hibernate.ejb.HibernatePersistence</provider> <!-- 添加持久化类 --> <class>entity.Customer</class> <properties> <!-- 连接数据库的基本信息 --> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> <property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/> <property name="javax.persistence.jdbc.user" value="root"/> <property name="javax.persistence.jdbc.password" value="root"/> <!-- 配置 JPA 实现产品的基本属性. 配置 hibernate 的基本属性 --> <property name="hibernate.format_sql" value="true"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.hbm2ddl.auto" value="update"/> </properties> </persistence-unit> </persistence>
JPA的基本注解:
@Entity
指出该Java为实体类,将其映射到指定的数据库表
@Table
用于当实体类名和数据库表名不一样,一般放在@Entity之前
name属性:指定在数据库中的表名
@Id
用于指定该属性是数据库的主键列,可以用于属性,也可用于getter方法之前
@GeneratedValue
主键的生成策略
@Basic
表示该属性需要映射成为数据库中的列,默认每个getter方法都有
fetch表示该属性的读取策略,有EAGER、LAZY两种
optional属性表示该属性是否为null,默认为true
@Column(name="LAST_NAME",length=50,nullable=false)
当数据库列和属性不一致时候使用
@Transient
表示该属性不映射到数据库列
@Temporal(TemporalType.DATE)
对日期精度的处理
用表来生成主键
将当前主键的值保存到一个数据库表中,主键的值每次都是从指定的表中查询获取
这种方法的生成主键的策略使用于任何数据库,不会造成数据库的不兼容问题
@Id @GeneratedValue(strategy=GenerationType.TABLE, generator="id_generate") @TableGenerator(name="id_generate", table="id_generator", pkColumnName="pk_name", pkColumnValue="customer_id", valueColumnName="pk_value", allocationSize=10) private Integer id;
Persistence:用于获取EntityManagerFactory实例
//1、创建EntityManagerFactory EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpa");EntityManagerFactory用来创建EntityManager
//2、创建EntityManager EntityManager entityManager = entityManagerFactory.createEntityManager();EntityManager:
查询方法:
@Test public void getReference(){ //类似于 hibernate 中 Session 的 load 方法(懒加载) Customer customer = entityManager.getReference(Customer.class, 10); System.out.println(customer.getClass().getName());//entity.Customer_$$_javassist_0 System.out.println(customer); } @Test public void find() { //类似于 hibernate 中 Session 的 get 方法. (立即加载) Customer customer = entityManager.find(Customer.class, 10); System.out.println(customer); }持久化操作:
@Test public void persistence() { //类似于 hibernate 的 save 方法. 使对象由临时状态变为持久化状态. //和 hibernate 的 save 方法的不同之处: 若对象有 id, 则不能执行 insert 操作, 而会抛出异常. Customer customer = new Customer(null, "Jacky", "jacky@qq.com", 35); entityManager.persist(customer); System.out.println(customer); }删除:
@Test public void remove() { //类似于 hibernate 中 Session 的 delete 方法. 把对象对应的记录从数据库中移除 //但注意: 该方法只能移除 持久化 对象. 而 hibernate 的 delete 方法实际上还可以移除 游离对象. Customer customer = entityManager.find(Customer.class, 50); entityManager.remove(customer); }merge方法:
@Test public void merge4() { Customer customer = new Customer(70, "Bob_update", "bobupdate@qq.com", 105); Customer customer2 = entityManager.find(Customer.class, 70); //若传入的是一个游离对象, 即传入的对象有 OID. //1. 若在 EntityManager 缓存中有对应的对象 //2. JPA 会把游离对象的属性复制到查询到EntityManager 缓存中的对象中. //3. EntityManager 缓存中的对象执行 UPDATE. Customer merge = entityManager.merge(customer); System.out.println(customer == customer2);//false } @Test public void merge3() { Customer customer = new Customer(70, "Bob_update", "bob@qq.com", 105); //若传入的是一个游离对象, 即传入的对象有 OID. //1. 若在 EntityManager 缓存中没有该对象 //2. 若在数据库中也有对应的记录 //3. JPA 会查询对应的记录, 然后返回该记录对一个的对象, 再然后会把游离对象的属性复制到查询到的对象中. //4. 对查询到的对象执行 update 操作. Customer merge = entityManager.merge(customer); System.out.println(customer==merge);//false } @Test public void merge2() { Customer customer = new Customer(70, "Bob", "bob@qq.com", 15); //若传入的是一个游离对象, 即传入的对象有 OID. //1. 若在 EntityManager 缓存中没有该对象 //2. 若在数据库中也没有对应的记录 //3. JPA 会创建一个新的对象, 然后把当前游离对象的属性复制到新创建的对象中 //4. 对新创建的对象执行 insert 操作. Customer merge = entityManager.merge(customer); System.out.println(customer.getId()); System.out.println(merge.getId()); } @Test public void merge1() { //1. 若传入的是一个临时对象 //会创建一个新的对象, 把临时对象的属性复制到新的对象中, 然后对新的对象执行持久化操作. 所以 //新的对象中有 id, 但以前的临时对象中没有 id. Customer customer = new Customer(null, "Marry", "marry@qq.com", 15); Customer merge = entityManager.merge(customer); System.out.println(customer.getId());//null System.out.println(merge.getId()); }flush()同步持久上下文环境,即将持久上下文环境的所有未保存实体的状态信息保存到数据库
@Test public void refresh(){ Customer customer = entityManager.find(Customer.class, 10); customer = entityManager.find(Customer.class, 10); //同 hibernate 中 Session 的 refresh 方法. entityManager.refresh(customer); } @Test public void flush() { Customer customer = entityManager.find(Customer.class, 10); System.out.println(customer); customer.setEmail(customer.getEmail()+"update"); //同 hibernate 中 Session 的 flush 方法. entityManager.flush(); System.out.println(customer); }
映射单向多对一的关联关系
@Test public void manyToOne() { Order order1 = new Order(); order1.setOrderName("O-BB-1"); Order order2 = new Order(); order2.setOrderName("O-BB-2"); Customer customer = new Customer(null, "Bob", "bob@qq.com", 24); order1.setCustomer(customer); order2.setCustomer(customer); //保存多对一时, 建议先保存 1 的一端, 后保存 n 的一端, 这样不会多出额外的 UPDATE 语句. entityManager.persist(customer);//此时3个insert语句 entityManager.persist(order1); entityManager.persist(order2); entityManager.persist(customer);//此时3个insert语句,2个update语句 }查询:
@ManyToOne(fetch=FetchType.LAZY) @Test public void find() { //默认情况下, 使用左外连接的方式来获取 n 的一端的对象和其关联的 1 的一端的对象. //可使用 @ManyToOne 的 fetch 属性来修改默认的关联属性的加载策略 Order order = entityManager.find(Order.class, 1);//默认情况,发送所外连接的语句一条语句 System.out.println(order.getOrderName()); System.out.println(order.getCustomer().getLastName()); }
单向一对多:
保存:
@Test public void save() { Customer customer = new Customer(); customer.setAge(18); customer.setEmail("AA@163.com"); customer.setLastName("AA"); Order order1 = new Order(); order1.setOrderName("O-AA-1"); Order order2 = new Order(); order2.setOrderName("O-AA-2"); //建立关联关系 customer.getOrders().add(order1); customer.getOrders().add(order2); //单向 1-n 关联关系执行保存时, 一定会多出 UPDATE 语句. //因为 n 的一端在插入时不会同时插入外键列. //执行保存操作 entityManager.persist(customer); //3个insert语句,2个update语句,一方必须维护关系 entityManager.persist(order1); entityManager.persist(order2); }查找:
@Test public void find() { Customer customer = entityManager.find(Customer.class, 1); System.out.println(customer.getLastName()); //默认对关联的多的一方使用懒加载的加载策略. //可以使用 @OneToMany 的 fetch 属性来修改默认的加载策略 System.out.println(customer.getOrders().size()); }删除:
@Test public void remove(){ //默认情况下, 若删除 1 的一端, 则会先把关联的 n 的一端的外键置空, 然后进行删除. //可以通过 @OneToMany 的 cascade 属性来修改默认的删除策略. Customer customer = entityManager.find(Customer.class, 1); entityManager.remove(customer); }
双向一对多:
新增:
@Test public void manyToOne() { Order order1 = new Order(); order1.setOrderName("O-CC-1"); Order order2 = new Order(); order2.setOrderName("O-CC-2"); Customer customer = new Customer(null, "CC", "CC@qq.com", 24); order1.setCustomer(customer); order2.setCustomer(customer); customer.getOrders().add(order1); customer.getOrders().add(order2); //在进行双向 1-n 关联关系时, 建议使用 n 的一方来维护关联关系, 而 1 的一方不维护关联系, 这样会有效的减少 SQL 语句. //注意: 若在 1 的一端的 @OneToMany 中使用 mappedBy 属性, 则 @OneToMany 端就不能再使用 @JoinColumn 属性了. entityManager.persist(customer);//此时3个insert语句,2个update语句 entityManager.persist(order1); entityManager.persist(order2); //entityManager.persist(customer);//此时3个insert语句,4个update语句 }
双向一对一:
@Test public void save() { Manager mgr = new Manager(); mgr.setMgrName("M-AA"); Dept dept = new Dept(); dept.setDeptName("D-AA"); //设置关联关系 mgr.setDept(dept); dept.setManager(mgr); //执行保存操作 //双向 1-1 的关联关系, 建议先保存不维护关联关系的一方, 即没有外键的一方, 这样不会多出 UPDATE 语句. entityManager.persist(mgr); entityManager.persist(dept); } @Test public void find(){ //1.默认情况下, 若获取维护关联关系的一方, 则会通过左外连接获取其关联的对象. //但可以通过 @OntToOne 的 fetch 属性来修改加载策略. Dept dept = entityManager.find(Dept.class, 1); System.out.println(dept.getDeptName()); //1. 默认情况下, 若获取不维护关联关系的一方, 则也会通过左外连接获取其关联的对象. //可以通过 @OneToOne 的 fetch 属性来修改加载策略. 但依然会再发送 SQL 语句来初始化其关联的对象 //这说明在不维护关联关系的一方, 不建议修改 fetch 属性. System.out.println(dept.getManager().getClass().getName()); }
双向多对多:
@ManyToMany(mappedBy="categorys") private Set<Item> items = new HashSet<>();//使用 @ManyToMany 注解来映射多对多关联关系 //使用 @JoinTable 来映射中间表 //1. name 指向中间表的名字 //2. joinColumns 映射当前类所在的表在中间表中的外键 //2.1 name 指定外键列的列名 //2.2 referencedColumnName 指定外键列关联当前表的哪一列 //3. inverseJoinColumns 映射关联的类所在中间表的外键 @ManyToMany @JoinTable(name = "item_category", joinColumns = {@JoinColumn(name = "item_id", referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "categoty_id", referencedColumnName = "id")}) private Set<Category> categorys = new HashSet<>();
二级缓存:加入包、配置文件
@Entity @Table(name = "jpa_customer") @Cacheable(true) public class Customer implements Serializable <!-- 配置二级缓存的策略 ALL:所有的实体类都被缓存 NONE:所有的实体类都不被缓存. ENABLE_SELECTIVE:标识 @Cacheable(true) 注解的实体类将被缓存 DISABLE_SELECTIVE:缓存除标识 @Cacheable(false) 以外的所有实体类 UNSPECIFIED:默认值,JPA 产品默认值将被使用 --> <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode> <!-- 二级缓存相关 --> <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"/> @Test public void secondLevelCache(){ Customer customer1 = entityManager.find(Customer.class, 1); transaction.commit(); entityManager.close(); entityManager = entityManagerFactory.createEntityManager(); transaction = entityManager.getTransaction(); transaction.begin(); Customer customer2 = entityManager.find(Customer.class, 1); }
JPQL语言
@Test public void jpql() { String jpql = "FROM Customer c WHERE c.age > ?"; Query query = entityManager.createQuery(jpql); // 占位符的索引是从 1 开始 query.setParameter(1, 20); List<Customer> customers = query.getResultList(); System.out.println(customers);// [Customer [id=1, lastName=CC, email=CC@qq.com, age=24], Customer [id=3, } @Test public void partlyProperties() { // 默认情况下, 若只查询部分属性, 则将返回 Object[] 类型的结果. 或者 Object[] 类型的 List. // 也可以在实体类中创建对应的构造器, 然后再 JPQL 语句中利用对应的构造器返回实体类的对象. String jpql = "SELECT new Customer(c.lastName, c.age) FROM Customer c WHERE c.id > ?"; List result = entityManager.createQuery(jpql).setParameter(1, 1).getResultList(); System.out.println(result);// [Customer [id=null, lastName=AA, email=null, age=13], Customer [id=null, // lastName=dd, email=null, age=23]] } @NamedQuery(name="testNamedQuery", query="FROM Customer c WHERE c.id = ?") @Entity @Table(name = "jpa_customer") @Cacheable(true) public class Customer implements Serializable @Test public void namedQuery(){ //createNamedQuery 适用于在实体类前使用 @NamedQuery 标记的查询语句 Query query = entityManager.createNamedQuery("testNamedQuery").setParameter(1, 3); Customer customer = (Customer) query.getSingleResult(); System.out.println(customer);//Customer [id=3, lastName=dd, email=dd, age=23] } @Test public void nativeQuery(){ //createNativeQuery 适用于本地 SQL String sql = "SELECT age FROM jpa_customer WHERE id = ?"; Query query = entityManager.createNativeQuery(sql).setParameter(1, 3); Object result = query.getSingleResult(); System.out.println(result); }
查询缓存:
@Test public void queryCache(){ //使用 hibernate 的查询缓存. String jpql = "FROM Customer c WHERE c.age > ?"; Query query = entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true); //占位符的索引是从 1 开始 query.setParameter(1, 1); List<Customer> customers = query.getResultList(); System.out.println(customers.size()); query = entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true); //占位符的索引是从 1 开始 query.setParameter(1, 1); customers = query.getResultList(); System.out.println(customers.size()); }
order by:
@Test public void orderBy(){ String jpql = "FROM Customer c WHERE c.age > ? ORDER BY c.age DESC"; Query query = entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true); //占位符的索引是从 1 开始 query.setParameter(1, 1); List<Customer> customers = query.getResultList(); System.out.println(customers.size()); }
group by :
@Test public void groupBy(){ //查询 order 数量大于 2 的那些 Customer String jpql = "SELECT o.customer FROM Order o " + "GROUP BY o.customer " + "HAVING count(o.id) >= 2"; List<Customer> customers = entityManager.createQuery(jpql).getResultList(); System.out.println(customers); }
关联查询:
@Test public void leftOuterJoinFetch(){ String jpql = "FROM Customer c LEFT OUTER JOIN FETCH c.orders WHERE c.id = ?"; //JPQL 的关联查询同 HQL 的关联查询 Customer customer = (Customer) entityManager.createQuery(jpql).setParameter(1, 1).getSingleResult(); //System.out.println(customer.getLastName()); //System.out.println(customer.getOrders().size()); List<Object[]> result = entityManager.createQuery(jpql).setParameter(1, 1).getResultList(); System.out.println(result); }
子查询:
@Test public void subQuery(){ //查询所有 Customer 的 lastName 为 YY 的 Order String jpql = "SELECT o FROM Order o " + "WHERE o.customer = (SELECT c FROM Customer c WHERE c.lastName = ?)"; Query query = entityManager.createQuery(jpql).setParameter(1, "AA"); List<Order> orders = query.getResultList(); System.out.println(orders.size()); }
内置函数:
@Test public void jpqlFunction(){ String jpql = "SELECT upper(c.email) FROM Customer c"; List<String> emails = entityManager.createQuery(jpql).getResultList(); System.out.println(emails); }
spring和jpa的整合:
<!-- 配置自动扫描的包 --> <context:component-scan base-package="jpa"></context:component-scan> <!-- 配置 C3P0 数据源 --> <context:property-placeholder location="classpath:db.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <!-- 配置其他属性 --> </bean> <!-- 配置 EntityManagerFactory --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"></property> <!-- 配置 JPA 提供商的适配器. 可以通过内部 bean 的方式来配置 --> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean> </property> <!-- 配置实体类所在的包 --> <property name="packagesToScan" value="jpa.spring.entities"></property> <!-- 配置 JPA 的基本属性. 例如 JPA 实现产品的属性 --> <property name="jpaProperties"> <props> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> </bean> <!-- 配置 JPA 使用的事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"></property> </bean> <!-- 配置支持基于注解是事务配置 --> <tx:annotation-driven transaction-manager="transactionManager"/> @Repository public class PersonDao { @PersistenceContext private EntityManager entityManager; public void save(Person person) { entityManager.persist(person); } }
转载于:https://www.cnblogs.com/lzb0803/p/8969760.html
