JPA(一)

一、JPA是什么?
Java Persistence API:用于对象持久化的 API
Java EE 5.0 平台标准的 ORM 规范,使得应用程序以统一的方式访问持久层
JPA和hibernate之间的关系
JPA 是 hibernate 的一个抽象(就像JDBC和JDBC驱动的关系):
JPA 是规范:JPA 本质上就是一种 ORM 规范,不是ORM 框架 —— 因为 JPA 并未提供 ORM 实现,它只是制订了一些规范,提供了一些编程的 API 接口,但具体实现则由 ORM 厂商提供实现
Hibernate 是实现:Hibernate 除了作为 ORM 框架之外,它也是一种 JPA 实现
从功能上来说, JPA 是 Hibernate 功能的一个子集
JPA供应商
JPA 的目标之一是制定一个可以由很多供应商实现的 API,目前Hibernate 3.2+、TopLink 10.1+ 以及 OpenJPA 都提供了 JPA 的实现
Hibernate
JPA 的始作俑者就是 Hibernate 的作者
Hibernate 从 3.2 开始兼容 JPA
OpenJPA
OpenJPA 是 Apache 组织提供的开源项目
TopLink
TopLink 以前需要收费,如今开源了

JPA的优势

1·标准化: 提供相同的 API,这保证了基于JPA 开发的企业应用能够经过少量的修改就能够在不同的 JPA 框架下运行。
2·简单易用,集成方便: JPA 的主要目标之一就是提供更加简单的编程模型,在 JPA 框架下创建实体和创建 Java 类一样简单,只需要使用 javax.persistence.Entity 进行注释;JPA 的框架和接口也都非常简单,
3·可媲美JDBC的查询能力: JPA的查询语言是面向对象的,JPA定义了独特的JPQL,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
4·支持面向对象的高级特性: JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,最大限度的使用面向对象的模型

JPA 包括 3方面的技术

1·ORM 映射元数据:JPA 支持 XML
和 JDK 5.0 注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中。
2·JPA 的 API:用来操作实体对象,执行CRUD操作,框架在后台完成所有的事情,开发者从繁琐的 JDBC和 SQL代码中解脱出来。
3·查询语言(JPQL):这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序和具体的 SQL 紧密耦合。

persistence.xml
配置信息:
- 配置什么ORM产品,来实现JPA【1,实际上配置的是 javax.persistence.spi.PersistenceProvider接口 2,若JPA项目中只有一个JPS实现产品,则也可以不配置该节点。】
- 配置数据库连接信息
- 配置JPA实现产品的基本属性(配置hiBenate的基本属性)
其中:
transaction-type:指定JPA的事务处理策略,属性值:(下面2种)
RESOURCE_LOCAL:数据库级别的事务,只能支持一种数据库。
JTA:支持分布式事务

使用JPA持久化对象的步骤

1·创建 persistence.xml, 在这个文件中配置持久化单元
2·需要指定跟哪个数据库进行交互;
3·需要指定 JPA 使用哪个持久化的框架以及配置该框架的基本属性
4·创建实体类, 使用 annotation 来描述实体类跟数据库表之间的映射关系.
5·使用 JPA API 完成数据增加、删除、修改和查询操作
6·创建 EntityManagerFactory (对应 Hibernate 中的 SessionFactory);
7·创建 EntityManager (对应 Hibernate 中的Session);


二、创建表的代码
这里写图片描述

package com.lq.jpa.demo1;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class Main {
    public static void main(String[] args) {
        /*
         *  - 创建EntityManagerFactory
             - 创建EntityManager
             - 开启事物
             - 进行持久化事物
             - 提交事务
             - 关闭EntityManager
             - 关闭EntityManagerFactory
         */
        //创建EntityManagerFactory EntityManager类似于Hibernate的sessionFactory
        String persistenceUnitName = "jpa-1";
        EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnitName);
        EntityManager em = emf.createEntityManager();
        //开启事物
        EntityTransaction  transaction =   em.getTransaction();
        transaction.begin();
        //进行持久化事物
        Customer c=  new Customer();
        c.setAge(1);
        c.setEmail("[email protected]");
        c.setLastName("哈喽JPA");
        //数据保存
        em.persist(c);
        //提交事务
        transaction.commit();
        //关闭
        em.close();
        emf.close();
    }
}

//Customer类
//table如果没有写,表名就是实体类的名称
//get方法上面加入列名。其他可以不写,默认添加了@Basic注解,列名是实体类属性名
@Entity
@Table(name="JPA_CUSTOMERS")
public class Customer {

    private Integer id;
    private String lastName;

    private String email;
    private int age;


    @GeneratedValue(strategy=GenerationType.AUTO)
    @Id
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    @Column(name="LAST_NAME",length=50,nullable=false)
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Temporal(TemporalType.TIMESTAMP)
    public Date getCreatedTime() {
        return createdTime;
    }
    public void setCreatedTime(Date createdTime) {
        this.createdTime = createdTime;
    }
    @Temporal(TemporalType.DATE)//调整时间精度
    public Date getBirth() {
        return birth;
    }
    public void setBirth(Date birth) {
        this.birth = birth;
    }
    //@Transient注解:这个属性不是数据库表中的字段。必须加这个注解,否则运行不通过。
    @Transient
    public String getinfo() {
        return "info";
    }
}

@Entity
@Table(name=”JPA_CUSTOMERS”)
//调整时间精度
@Temporal(TemporalType.TIMESTAMP)
@Temporal(TemporalType.DATE)
//@Transient注解:这个属性不是数据库表中的字段。必须加这个注解,否则运行不通过。
@Transient
//自增主键【不用set值,否则出错】
@GeneratedValue(strategy=GenerationType.AUTO)
@Id

JPA的测试,init()中先进行三个对象的建立以及事物的开启,destory()中进行事物的提交,和2个对象的关闭。测试方法中,对JPA操作数据库的方法进行测试。前面2个方法,加上@Before和@After注解

EntityManager:
在JPA规范中,EntityManager是完成持久化操作的核心对象。在调用EntityManager:将实体(也就是普通java对象)
将其持久化后,变成持久化对象。EntityManager对象在实体类和数据源之间进行映射管理,可以来管理
更新Entity Bean,根据主键查找Entity Bean,还可以根据JPQL语句查询实体。
实体的状态有:
1.新建状态:新建的对象,尚未拥有持久性主键。
2.持久性状态:已经拥有持久性主键,和持久化建立上下文环境
3.游离状态:拥有持久化主键,但是没有和持久化,建立上下文环境。
4.删除状态:拥有持久化主键,已经和持久化建立上下文环境,但是从数据库删除。

==============================
entityManager.merge()类似于hibernate中session的saveAndUpdate()
1.传入的是临时对象
把临时对象【没有设置id属性值】持久化到数据库:
首先创建一个临时对象,并进行赋值,把对象传入merge()方法中,生成持久化对象。
临时对象没有id属性值,持久化对象有id值。

2.传入的是游离对象【设置有id属性值】
即传入的对象有 OID.
//1. 若在 EntityManager 缓存中没有该对象【如果有对象,游离对象属性复制到缓存中,对缓存中的对象,进行update】
//2. 若在数据库中也没有对应的记录【如果有记录,复制到新对象中,进行update操作】
//3. JPA 会创建一个新的对象, 然后把当前游离对象的属性复制到新创建的对象中
//4. 对新创建的对象执行 insert 操作.

【2个ID相同的对象,进行关联,JPA可以,它中有一个复制的功能,entityManager始终是对一个对象进行关联。

hibernate中session不能和2个对象进行关联,会报错。】

flush方法同hibernate中session的flush()方法,会发送update语句,但是没有提交。

reflush方法同session中的reflush方法。

JDQL查询时:EntityManager 使用的方法有:
createQuery(String sqlString)创建一个查询对象
createNamedQuery(String name)根据命名查询语句,创建查询对象。
createNativeQuery(String sqlString)使用标准SQL语句

createNativeQuery(String sqlString,String resultSetMapping)使用标准SQL,并指定结果集Map的名称。

EntityTransaction接口管理事务。
三、table主键生成策略
2种方式:
第一:上面代码所示,数据库自

增的方式,生成的简单逐1递增的主键
第二:如下所示,通过第2张表来生成主键。自定义步长。
【创建第二张表,和实体类中方法注解】
这里写图片描述
这里写图片描述

    @TableGenerator(name="ID_GENERATOR",
            table="jpa_id_generators",
            pkColumnName="PK_NAME",//列
            pkColumnValue="CUSTOMER_ID",//行
            valueColumnName="PK_VALUE",//点
            allocationSize=100)
    @GeneratedValue(strategy=GenerationType.TABLE,generator="ID_GENERATOR")
    //@GeneratedValue(strategy=GenerationType.AUTO)
    @Id
    public Integer getId() {
        return id;
    }

1,类似于get方法的find()方法。使用类似load的方法,getReference()具体使用到属性的时候,才向数据库发送sql。
在使用的属性的时候,会创建代理对象,出现懒加载异常问题。因为他底层使用的是hibernate相应的方法。
2,persist()方法,类似hibernate的save()方法,把对象从临时状态,转化为持久化状态。
这里:如果对象中有id属性,不能执行insert操作,否则会抛出异常。但是在hibernate中,没问题。
3,entityManger.remove()方法,只能移除持久化对象,而hibernate可以移除游离对象和持久化对象。
这里写图片描述
三、多对一关系映射
实体类:创建多的一方的表:

private Customer customer;
//映射单向 n-1 的关联关系
    //使用 @ManyToOne 来映射多对一的关联关系
    //使用 @JoinColumn 来映射外键. 
    //可使用 @ManyToOne 的 fetch 属性来修改默认的关联属性的加载策略
    @JoinColumn(name="CUSTOMER_ID")
    @ManyToOne(fetch=FetchType.LAZY)
    public Customer getCustomer() {
        return customer;
    }

测试类:

//删除时候,不能删除1 的一端。

//默认情况下, 使用左外连接的方式来获取 n 的一端的对象和其关联的 1 的一端的对象. 
    //可使用 @ManyToOne 的 fetch 属性来修改默认的关联属性的加载策略
    @Test
    public void testManyToOneFind(){
        Order order = entityManager.find(Order.class, 1);
        System.out.println(order.getOrderName());           System.out.println(order.getCustomer().getLastName());
    }

四、单向一对多映射
persist()
先保存1,再保存n。或者是相反的顺序,都没有用的。
一定会出现多个update语句,因为n端的插入时,不会插入外键列。
find()
对关联的多的一方,默认使用懒加载的策略。
可以通过@OneToMany中fetch属性,来修改加载策略。
remove()
删除1的一方,默认情况下,先把外键值设置null,再删除n表中的记录。
可以通过@OneToMany中cascade属性,来修改删除策略。
update()

@Test
    public void testUpdate(){
        Customer customer = entityManager.find(Customer.class, 10);
        customer.getOrders().iterator().next().setOrderName("O-XXX-10");
    }

五、双向多对一映射
保证实体类里面属性 列名设置一样。
不维护外间关系,直接操作数据库,会出现多条SQL语句的麻烦。
解决:
由多的一方来维护外键关系,

//1端的类:
    private Set<Order> orders = new HashSet<>();
    //映射单向 1-n 的关联关系
    //使用 @OneToMany 来映射 1-n 的关联关系
    //使用 @JoinColumn 来映射外键列的名称
    //可以使用 @OneToMany 的 fetch 属性来修改默认的加载策略
    //可以通过 @OneToMany 的 cascade 属性来修改默认的删除策略. 
    //注意: 若在 1 的一端的 @OneToMany 中使用 mappedBy 属性, 则 @OneToMany 端就不能再使用 @JoinColumn 属性了.(mappedBy 中是 多的一端里面的属性customer,多的一方维护。自己不维护。)
//  @JoinColumn(name="CUSTOMER_ID")
    @OneToMany(fetch=FetchType.LAZY,cascade={CascadeType.REMOVE},mappedBy="customer")
    public Set<Order> getOrders() {
        return orders;
    }

六、双向一对一映射
Manager .java

//对于不维护关联关系, 没有外键的一方, 使用 @OneToOne 来进行映射, 建议设置 mappedBy=true,有外键的一方维护关联关系,自己不维护。
//先保存不维护外键关系的一方。就是 先保存没有外键的一方。
    @OneToOne(mappedBy="mgr")
    public Department getDept() {
        return dept;
    }

Department.java

//使用 @OneToOne 来映射 1-1 关联关系。
    //若需要在当前数据表中添加主键则需要使用 @JoinColumn 来进行映射. 注意, 1-1 关联关系, 所以需要添加 unique=true
    @JoinColumn(name="MGR_ID", unique=true)
    @OneToOne(fetch=FetchType.LAZY)
    public Manager getMgr() {
        return mgr;
    }
//1. 默认情况下, 若获取不维护关联关系的一方, 则也会通过左外连接获取其关联的对象. 
    //可以通过 @OneToOne 的 fetch 属性来修改加载策略. 但依然会再发送 SQL 语句来初始化其关联的对象
    //这说明在不维护关联关系的一方, 不建议修改 fetch 属性. 
    @Test
    public void testOneToOneFind2(){
        Manager mgr = entityManager.find(Manager.class, 1);
        System.out.println(mgr.getMgrName());
        System.out.println(mgr.getDept().getClass().getName());
    }

七、多对多的保存

    //Category 类
    private Set<Item> items = new HashSet<>();
    @ManyToMany(mappedBy="categories")
    public Set<Item> getItems() {
        return items;
    }
//Item 类:
private Set<Category> categories = new HashSet<>();
//使用 @ManyToMany 注解来映射多对多关联关系
    //使用 @JoinTable 来映射中间表
    //1. name 指向中间表的名字
    //2. joinColumns 映射当前类所在的表在中间表中的外键
    //2.1 name 指定外键列的列名
    //2.2 referencedColumnName 指定外键列关联当前表的哪一列
    //3. inverseJoinColumns 映射关联的类所在中间表的外键
    @JoinTable(name="ITEM_CATEGORY",
            joinColumns={@JoinColumn(name="ITEM_ID", referencedColumnName="ID")},
            inverseJoinColumns={@JoinColumn(name="CATEGORY_ID", referencedColumnName="ID")})
    @ManyToMany
    public Set<Category> getCategories() {
        return categories;
    }
//测试类:
//对于关联的集合对象, 默认使用懒加载的策略.
    //使用维护关联关系的一方获取, 还是使用不维护关联关系的一方获取, SQL 语句相同. 
    @Test
    public void testManyToManyFind(){
//      Item item = entityManager.find(Item.class, 5);
//      System.out.println(item.getItemName());
//      
//      System.out.println(item.getCategories().size());

        Category category = entityManager.find(Category.class, 3);
        System.out.println(category.getCategoryName());
        System.out.println(category.getItems().size());
    }

八、二级缓存

首先,persistence.xml中进行配置,和hibernate的二级缓存,配置相同。
步骤:导包,添加一个新的配置文件,配置中添加代码,实体类上添加注解@Cacheable(true)。
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-1" transaction-type="RESOURCE_LOCAL">
        <!-- 
        配置使用什么 ORM 产品来作为 JPA 的实现 
        1. 实际上配置的是  javax.persistence.spi.PersistenceProvider 接口的实现类
        2. 若 JPA 项目中只有一个 JPA 的实现产品, 则也可以不配置该节点. 
        -->
        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <!-- 添加持久化类 -->
        <class>com.atguigu.jpa.helloworld.Customer</class>
        <class>com.atguigu.jpa.helloworld.Order</class>

        <class>com.atguigu.jpa.helloworld.Department</class>
        <class>com.atguigu.jpa.helloworld.Manager</class>

        <class>com.atguigu.jpa.helloworld.Item</class>
        <class>com.atguigu.jpa.helloworld.Category</class>

        <!-- 
        配置二级缓存的策略 【放在后面】
        ALL:所有的实体类都被缓存
        NONE:所有的实体类都不被缓存. 
        ENABLE_SELECTIVE:标识 @Cacheable(true) 注解的实体类将被缓存
        DISABLE_SELECTIVE:缓存除标识 @Cacheable(false) 以外的所有实体类
        UNSPECIFIED:默认值,JPA 产品默认值将被使用
        -->
        <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>

        <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="1230"/>

            <!-- 配置 JPA 实现产品的基本属性. 配置 hibernate 的基本属性 -->
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>

            <!-- 二级缓存相关 -->
            <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"/>
        </properties>
    </persistence-unit>
</persistence>

这里写图片描述
这里写图片描述

猜你喜欢

转载自blog.csdn.net/Estelle_ya/article/details/82710802