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

     

     

  • 相关阅读:
    【问题记录】IIS配置项
    Dapr可观测性
    es6 set方法使用
    js 数据类型
    获取到select下的所有option的文字和值
    使用js的webrtc进行sip协议连接,实现webrtc与电话网打通
    Qt (QGis) 中动态布局例子
    Latex中使注脚首行不缩进,且新行与首行对齐
    [转] 控制域的更新方式_小强office
    访问被屏蔽的FTP网站
  • 原文地址:https://www.cnblogs.com/lzb0803/p/8969760.html
Copyright © 2020-2023  润新知