JPA

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);
    }
}

 

 

 

 

posted @ 2018-04-28 21:58  coderlzb  阅读(286)  评论(0编辑  收藏  举报