JPA規(guī)范

[TOC]

ORM思想

ORM全稱Object Relational Mapping,即對象關(guān)系映射,是一種程序設(shè)計技術(shù),用于實現(xiàn)面向?qū)ο缶幊陶Z言里不同類型系統(tǒng)的數(shù)據(jù)之間的轉(zhuǎn)換。

通俗點講,用來把對象映射到基于sql的關(guān)系模型數(shù)據(jù)庫結(jié)構(gòu)中去。這樣,我們在具體的操作實體對象的時候,就不需要再去和復(fù)雜的sql語句打交道,只需簡單的操作實體對象的屬性和方法。ORM技術(shù)是在對象和關(guān)系之間提供了一條橋梁,前臺的對象型數(shù)據(jù)和數(shù)據(jù)庫中的關(guān)系型的數(shù)據(jù)通過這個橋梁來相互轉(zhuǎn)化 。

JPA規(guī)范

JPA(Java持久化API)是一種Java應(yīng)用程序接口規(guī)范,描述java應(yīng)用中關(guān)系數(shù)據(jù)的管理,充當(dāng)面向?qū)ο蟮念I(lǐng)域模型和關(guān)系數(shù)據(jù)庫系統(tǒng)之間的橋梁。

Application code ---> JPA ---->實現(xiàn)JPA規(guī)范的框架(比如hibernate、spring data jpa) ----> 數(shù)據(jù)庫

graph LR

A[Application code] --> |jpa規(guī)范| B(hibernate)
B -->|jdbc| C(mysql數(shù)據(jù)庫)

hibernate框架

hibernate是一個開源的對象關(guān)系映射框架,它對jdbc進行了非常輕量級的對象封裝,它將POJO與數(shù)據(jù)庫表建立映射關(guān)系,是一個全自動的orm框架,hibernate可以自動生成sql語句,自動執(zhí)行,使得java程序員可以隨心所欲的使用對象編程思維來操縱數(shù)據(jù)庫。

入門案例

1、創(chuàng)建項目工程,導(dǎo)入相關(guān)依賴

    compile group: 'org.hibernate', name: 'hibernate-entitymanager', version: '5.4.3.Final'
    compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.16'
    compile group: 'org.hibernate', name: 'hibernate-c3p0', version: '5.4.3.Final'

2、配置jpa的核心配置文件

  • 配置到路徑下的一個叫做META-INF的文件夾下
  • 文件名字必須為 persistence.xml
    • 必須配置persistence-unit(持久化單元)節(jié)點
    • name:持久化單元名稱,自定義
    • transaction-type:事務(wù)管理的方式
      • JTA:分布式事務(wù)管理
      • RESOURCE_LOCAL:本地事務(wù)管理
    • 配置jpa的實現(xiàn)方式
    • 配置實體類
    • 配置數(shù)據(jù)庫信息
    • 配置jpa實現(xiàn)方的配置信息(可選)
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <!--必須配置persistence-unit節(jié)點-->
    <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
        <!--jpa實現(xiàn)方式-->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <!--配置實體類-->
        <class>com.lxf.User</class>
        <properties>
            <!--數(shù)據(jù)庫信息-->
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="crystal1024"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/study?serverTimezone=GMT"/>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>

            <!--jpa實現(xiàn)方的配置信息-->
            <!--日志顯示sql語句-->
            <property name="hibernate.show_sql" value="true"/>
            <!--
            自動創(chuàng)建數(shù)據(jù)庫的方式:
                create:程序運行時創(chuàng)建數(shù)據(jù)庫表,如果表存在,先刪除再創(chuàng)建
                update:程序運行時創(chuàng)建數(shù)據(jù)庫表,如果表存在,不會創(chuàng)建,表有改動的話會更新
                none:不會創(chuàng)建表
            -->
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
</persistence>

3、編寫實體類POJO

public class User {
    private Integer id;
    private String name;
    private Integer age;
    private Integer sex;//0未知1男2女
    private String address;
    private String phone;
    ...省略getter setter
}

4、配置實體類和表,類中屬性和表中字段的映射關(guān)系

  • 類與表的映射關(guān)系
    • @Entity:聲明實體類
    • @Table:配置實體類與表的映射關(guān)系
      • name:配置數(shù)據(jù)庫表的名稱
  • 屬性與表字段的映射關(guān)系
    • @Id:聲明主鍵的配置
    • @GeneratedValue:配置主鍵的生成策略
      • GenerationType.IDENTITY:自增,使用底層數(shù)據(jù)庫支持的自動增長方式對id自增(比如mysql)
      • GenerationType.SEQUENCE:序列,(比如oracle)
      • GenerationType.TABLE:jpa提供的一種機制,通過一張數(shù)據(jù)庫表的形式幫助我們完成主鍵自增
      • GenerationType.AUTO:由程序自動的幫助我們選擇主鍵生成策略
    • @Column:配置屬性與字段的映射關(guān)系
      • name:數(shù)據(jù)庫中表字段的名字
import javax.persistence.*;

@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;
    @Column(name = "name")
    private String name;
    @Column(name = "age")
    private Integer age;
    @Column(name = "sex")
    private Integer sex;//0未知1男2女
    @Column(name = "address")
    private String address;
    @Column(name = "phone")
    private String phone;
    ...省略getter setter
}

5、jpa操作數(shù)據(jù)庫

  • 加載配置文件,創(chuàng)建工廠(實體管理類工廠)對象。
    • Persistence:用于創(chuàng)建實體管理器工廠(EntityManagerFactory)
      • createEntityManagerFactory:根據(jù)持久化單元名稱創(chuàng)建實體管理器工廠
    • EntityManagerFactory:用于創(chuàng)建實體管理器對象(EntityManager),創(chuàng)建比較浪費資源,線程安全對象。所以一般會以靜態(tài)代碼塊的形式創(chuàng)建一個公共的EntityManagerFactory對象。
      • createEntityManager:內(nèi)部維護了數(shù)據(jù)庫信息、緩存、實體管理器對象等很多信息
  • 獲取實體管理器
    • EntityManager:實體管理器。
      • getTransaction:創(chuàng)建事務(wù)對象
      • persist:保存
      • merge:更新
      • remove:刪除
      • find/getRefrence:根據(jù)id查詢
  • 獲取事務(wù)對象,開啟事務(wù)
    • EntityTransaction:事務(wù)對象。
      • begin:開啟事務(wù)
      • commit:提交事務(wù)
      • rollback:回滾事務(wù)
  • 完成CRUD操作
  • 提交事務(wù)(異常時回滾事務(wù))
  • 釋放資源
    @Test
    public void testSave(){
        //1.加載配置文件,獲取工廠對象
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("myJpa");
        //2.創(chuàng)建實體管理器
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        //3.獲取事務(wù)對象
        EntityTransaction transaction = entityManager.getTransaction();
        //4.開啟事務(wù)
        transaction.begin();
        try {
            //5.相關(guān)CRUD操作
            User user = new User();
            user.setName("tom");
            user.setAge(18);
            user.setSex(1);
            //保存
            entityManager.persist(user);
            //6.提交事務(wù)
            transaction.commit();
        }catch (Exception e){
            //6.回滾事務(wù)
            transaction.rollback();
            throw new RuntimeException("出異常啦");
        }finally {
            //7.釋放資源
            entityManager.close();
            entityManagerFactory.close();
        }
    }

控制臺打印出的sql日志:

Hibernate: create table user (id integer not null auto_increment, address varchar(255), age integer, name varchar(255), phone varchar(255), sex integer, primary key (id)) engine=InnoDB

Hibernate: insert into user (address, age, name, phone, sex) values (?, ?, ?, ?, ?)

優(yōu)化EntityManagerFactory的創(chuàng)建

上面有提到,EntityManagerFactory的創(chuàng)建比較浪費資源,而且它是線程安全對象。所以一般會以靜態(tài)代碼塊的形式創(chuàng)建一個公共的EntityManagerFactory對象。

public class JPAUtil {
    private static EntityManagerFactory factory;
    
    static {
        factory = Persistence.createEntityManagerFactory("myJpa");
    }
    
    public static EntityManager getEntityManager(){
        return factory.createEntityManager();
    }
}

此時我們的代碼會變成這樣:

    @Test
    public void testSave(){
        EntityManager entityManager = JPAUtil.getEntityManager();
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        try {
            User user = new User();
            user.setName("tom");
            user.setAge(18);
            user.setSex(1);
            entityManager.persist(user);
            transaction.commit();
        }catch (Exception e){
            transaction.rollback();
            throw new RuntimeException("出異常啦");
        }finally {
            entityManager.close();
        }
    }

數(shù)據(jù)庫操作

插入操作

User user = new User();
user.setName("tom");
user.setAge(18);
user.setSex(1);
//接收要插入的對象
entityManager.persist(user);

刪除操作

//先查詢出我們要刪除的對象
User user = entityManager.getReference(User.class, 1);
//接收要刪除的對象
entityManager.remove(user);

更新操作

User user = entityManager.getReference(User.class, 2);
user.setName("lili");
//接收要修改的對象
entityManager.merge(user);

查詢操作

內(nèi)置API

根據(jù)主鍵查詢單個:

 /**
  * 參數(shù):
  *  1. class對象:查詢數(shù)據(jù)結(jié)果需要包裝的實體類類型的字節(jié)碼
  *  2. 主鍵  這里是id
  */
 User user = entityManager.find(User.class, 1);

 /**
  * 參數(shù):
  *  1. class對象:查詢數(shù)據(jù)結(jié)果需要包裝的實體類類型的字節(jié)碼
  *  2. 主鍵  這里是id
  */
 User user = entityManager.getReference(User.class, 1);

find和getReference兩者的區(qū)別:

  • find:立即加載
    • 獲取的對象就是要查詢的對象本身
    • 調(diào)用find方法時,會立即通過sql語句查詢數(shù)據(jù)庫
  • getReference:延遲加載(懶加載)
    • 獲取的對象是一個動態(tài)代理對象
    • 調(diào)用getReference方法并不會立即通過sql語句查詢數(shù)據(jù)庫,而是當(dāng)你使用這個對象的時候才會進行數(shù)據(jù)庫查詢

語句查詢

創(chuàng)建查詢對象Query

jpa提供了一系列create方法來獲取一個Query對象進行復(fù)雜查詢。

jpql全稱Java Persistence Query Language,java持久化查詢語言,它和sql的語法很像,不過操作的是類和屬性。

  • createQuery:接收jpql語句
String jpql = "select u.name from User u";
//String jpql = "select name from User";//和上面語句效果是一樣的
Query query = entityManager.createQuery(jpql);
  • createNamedQuery:執(zhí)行命名查詢,其實和createQuery是一樣的,提前定義好的jpql語句
@Entity
@Table(name = "user")
@NamedQuery(name = "queryName",query = "select u.name from User u")
public class User {
    ...
}

Query query = entityManager.createNamedQuery("queryName");
  • createNativeQuery:接收原生sql語句
Query query = entityManager.createNativeQuery("select u.name from user as u");

常用查詢操作

排序
  • sql:select * from user order by id desc
  • jpql:from User order by id desc
統(tǒng)計
  • sql:select count(id) from user
  • jpql:select count(id) from User
分頁
  • sql:select * from user limit 1,10
  • jpql:from User
String jpql = "from User";
Query query = entityManager.createQuery(jpql);
query.setFirstResult(1);
query.setMaxResults(10);
條件查詢
  • sql:select * from user where id > 1
  • jpql:from User where id > 1

Query的常用方法

  • executeUpdate:執(zhí)行更新和刪除操作。
  • getFirstResult:返回查詢對象設(shè)置為檢索的第一個定位結(jié)果。
  • getMaxResults:返回查詢對象設(shè)置為檢索的最大結(jié)果數(shù)。
  • getResultList:返回結(jié)果列表。
  • setFirstResult:分配要檢索的第一個結(jié)果的位置。
  • setMaxResults:分配要檢索的最大結(jié)果數(shù)。
  • setParameter:設(shè)置jpql語句中的占位符

Criteria查詢

Criteria查詢是在jpa 2.0的版本加入的一個更加符合面向?qū)ο笏季S的類型安全的查詢方式。

首先介紹幾個概念:

  • CriteriaBuilder:用于構(gòu)造過濾條件(Predicate),內(nèi)部提供了==大量的api==。在該接口中使用Predicate代替Expression <Boolean>可以解決java泛型向下兼容的問題。
  • CriteriaQuery<T>:定義頂級查詢的功能。內(nèi)部提供where、groupBy、orderby、having等方法。
  • Root<X>:根查詢通常指向?qū)嶓w。

舉個列子:select * from User where id > 3 and age >18

通俗點講,select * from User屬于Root,where屬于CriteriaQuery,查詢方式,id > 3 and age >18屬于CriteriaBuilder構(gòu)建出來的查詢條件,這么講可能不是很準(zhǔn)確,但很容易理解。

簡單翻譯一下上面的語句:

public class JpaCriteriaTest {
    @Test
    public void testCriteria(){
        EntityManager entityManager = JPAUtil.getEntityManager();
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        try {
            //拿到CriteriaBuilder、CriteriaQuery、Root三個對象
            CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
            CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class);
            Root<User> root = criteriaQuery.from(User.class);

            //構(gòu)建過濾條件
            Predicate idCondition = criteriaBuilder.greaterThan(root.get("id"), 3);
            Predicate ageCondition = criteriaBuilder.greaterThan(root.get("age"), 18);
            Predicate predicate = criteriaBuilder.and(idCondition, ageCondition);

            //組合查詢
            criteriaQuery.where(predicate);

            //查詢并獲取結(jié)果
            TypedQuery<User> typedQuery = entityManager.createQuery(criteriaQuery);
            List<User> resultList = typedQuery.getResultList();//得到id>3的所有數(shù)據(jù)
            resultList.forEach(new Consumer<User>() {
                @Override
                public void accept(User user) {
                    System.out.println(user);
                }
            });

            transaction.commit();
        }catch (Exception e){
            transaction.rollback();
            e.printStackTrace();
            throw new RuntimeException("出異常啦");
        }finally {
            entityManager.close();
        }
    }
}

如果我們只想查詢某幾個字段,可以使用CriteriaQuery<Tuple>:

//拿到CriteriaBuilder、CriteriaQuery、Root三個對象
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> tupleQuery = criteriaBuilder.createTupleQuery();
Root<User> root = tupleQuery.from(User.class);
tupleQuery.multiselect(root.get("name"),root.get("age"));

//構(gòu)建過濾條件
Predicate idCondition = criteriaBuilder.greaterThan(root.get("id"), 3);
Predicate ageCondition = criteriaBuilder.greaterThan(root.get("age"), 18);
Predicate predicate = criteriaBuilder.and(idCondition, ageCondition);

//組合查詢
tupleQuery.where(predicate);

//查詢并獲取結(jié)果
TypedQuery<Tuple> typedQuery = entityManager.createQuery(tupleQuery);
List<Tuple> resultList = typedQuery.getResultList();//得到id>3的所有數(shù)據(jù)
resultList.forEach(new Consumer<Tuple>() {
   @Override
   public void accept(Tuple tuple) {
      System.out.println(tuple.get(0,String.class));
       System.out.println(tuple.get(1,Integer.class));
   }
});

多表關(guān)系映射

一對多

  • 主表:一的一方為主表
  • 從表:多的一方為從表
  • 外鍵:需要再從從表上新建一列作為外鍵,它的取值來源于主表的主鍵

另外,一對一其實就是一種特殊的一對多。

案例

一個student表,一個school表,一個學(xué)??梢杂泻芏鄬W(xué)生,一個學(xué)生只屬于一個學(xué)校。

  • 表關(guān)系:一對多
    • 主表:school
    • 從表:student,我們需要在從表上添加外鍵
  • 編寫實體
    • School:除了自有屬性外應(yīng)該包含一個List<Student>。
    • Student:除了自有屬性外應(yīng)該有一個School屬性,一個外鍵字段。
  • 使用JPA注解配置映射關(guān)系:一般一方映射,另一方參照即可
    • @OneToMany:聲明一對多
      • targetEntity:目標(biāo)實體(從表實體)
      • mappedBy:參照映射
    • @ManyToOne:聲明多對一
      • targetEntity:目標(biāo)實體(從表實體)
    • @JoinColumn:配置外鍵
      • name:從表上的外鍵字段名稱
      • referencedColumnName:參照的主表主鍵字段名稱
@Entity
@Table
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Integer studentId;
    @Column
    private String name;
    @Column
    private Double score;
    @Column
    private Integer sex;
    @Column(name = "school_id",insertable = false,updatable = false)//外鍵字段
    private Integer schoolId;

    @ManyToOne(targetEntity = School.class)//配置多對一關(guān)系
    @JoinColumn(name = "school_id",referencedColumnName = "schoolId")//配置外鍵
    private School school;
    ...
}

@Entity
@Table
public class School {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer schoolId;
    @Column
    private String name;
    @Column
    private String address;

//    @OneToMany(targetEntity = Student.class)//聲明關(guān)系,一對多
//    @JoinColumn(name = "school_id",referencedColumnName = "schoolId")//配置外鍵
    @OneToMany(mappedBy = "school")//Student里面已經(jīng)配置了映射關(guān)系,表示參照Student里面的school
    private List<Student> students = new ArrayList<>();
    ...
}

保存測試

public class JpaOneToManyTest {

    @Test
    public void testSave(){
        EntityManager entityManager = JPAUtil.getEntityManager();
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        try {
            Student student = new Student();
            student.setName("石昊");
            School school = new School();
            school.setName("天神書院");

            //創(chuàng)建關(guān)系
            student.setSchool(school);
//            school.getStudents().add(student);//兩句代碼都可以創(chuàng)建關(guān)系

            entityManager.persist(school);//先保存主表,如果先保存從表的話最好需要多一個update操作設(shè)置從表外鍵字段值
            entityManager.persist(student);

            transaction.commit();
        }catch (Exception e){
            transaction.rollback();
            e.printStackTrace();
            throw new RuntimeException("出異常啦");
        }finally {
            entityManager.close();
        }
    }
}

刪除測試

  • 主表配置外鍵映射:這種情況下刪除會先將從表外鍵值設(shè)為null,再刪除主表數(shù)據(jù)。
  • 主表放棄外鍵維護,參照從表的映射:這種情況下只能使用級聯(lián)刪除。

級聯(lián)操作

操作一個對象的同時操作它的關(guān)聯(lián)對象。(先操作關(guān)聯(lián)對象)

我們使用cascade來配置級聯(lián)關(guān)系:

  • CascadeType.ALL:級聯(lián)所有操作
  • CascadeType.PERSIST:級聯(lián)保存操作
  • CascadeType.MERGE:級聯(lián)更新操作
  • CascadeType.REMOVE:級聯(lián)刪除操作
  • CascadeType.REFRESH:級聯(lián)refresh操作
  • CascadeType.DETACH:級聯(lián)detach操作
@ManyToOne(targetEntity = School.class,cascade = CascadeType.ALL)
@JoinColumn(name = "school_id",referencedColumnName = "schoolId")

級聯(lián)保存

Student student = new Student();
student.setName("葉凡");
School school = new School();
school.setName("荒古禁地");

//創(chuàng)建關(guān)系
 student.setSchool(school);

//這里只保存從表,主表會被級聯(lián)保存
entityManager.persist(school);

級聯(lián)刪除

@OneToMany(mappedBy = "school",cascade = CascadeType.ALL)
private List<Student> students = new ArrayList<>();
School school = entityManager.find(School.class, 10);
entityManager.remove(school);//此時主表和從表中相關(guān)的數(shù)據(jù)都會刪除

多對多

案例

一個developer表,一個language表,一個開發(fā)者可以會多種語言,一種語言也會有很多開發(fā)者會。

中間表:中間表中最少應(yīng)該由兩個字段組成,這兩個字段作為外鍵指向兩張表的主鍵,又組成了聯(lián)合主鍵。

  • 表關(guān)系:多對多
    • 開發(fā)者表
    • 語言表
    • 中間表:最少應(yīng)該由兩個字段組成,這兩個字段作為外鍵指向兩張表的主鍵,又組成了聯(lián)合主鍵。
  • 編寫實體
    • Develop:除了自有屬性外應(yīng)該包含一個List<Language>。
    • Language:除了自有屬性外應(yīng)該包含一個List<Develop>。
    • 中間表:可以建一個實體,也可以直接數(shù)據(jù)庫建表,一般用不到這個實體。
  • 使用JPA注解配置映射關(guān)系:一般一方映射,另一方參照即可
    • @ManyToMany:聲明多對多
      • targetEntity:目標(biāo)實體
    • @JoinTable:添加中間表
      • name:中間表名
      • joinColumns:添加一個@JoinColumn數(shù)組,表示==當(dāng)前對象==在中間表的外鍵
      • inverseJoinColumns:添加一個@JoinColumn數(shù)組,表示==對方對象==在中間表的外鍵
@Entity
@Table
public class Developer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Integer developerId;
    @Column
    private String name;

    @ManyToMany(targetEntity = Language.class)
    @JoinTable(name = "middle_develop_language",
            joinColumns = @JoinColumn(name = "develop_id", referencedColumnName = "developerId"),
            inverseJoinColumns = @JoinColumn(name = "language_id", referencedColumnName = "languageId"))
    private List<Language> languages = new ArrayList<>();
    ...
}


@Entity
@Table
public class Language {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Integer languageId;
    @Column
    private String des;

//    @ManyToMany(targetEntity = Developer.class)
//    @JoinTable(name = "middle_develop_language",
//            joinColumns = @JoinColumn(name = "language_id", referencedColumnName = "languageId"),
//            inverseJoinColumns = @JoinColumn(name = "develop_id", referencedColumnName = "developerId"))
    @ManyToMany(mappedBy = "languages")//放棄維護權(quán),參照對方的映射關(guān)系
    private List<Developer> developers = new ArrayList<>();
    private List<Developer> developers = new ArrayList<>();
    ...
}

保存測試

public class JpaManyToManyTest {

    @Test
    public void testSave(){
        EntityManager entityManager = JPAUtil.getEntityManager();
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        try {
            Developer developer = new Developer();
            developer.setName("lxf");
            Language language = new Language();
            language.setDes("java");

            //創(chuàng)建關(guān)系
            developer.getLanguages().add(language);

            entityManager.persist(developer);
            entityManager.persist(language);

            transaction.commit();
        }catch (Exception e){
            transaction.rollback();
            e.printStackTrace();
            throw new RuntimeException("出異常啦");
        }finally {
            entityManager.close();
        }
    }
}

級聯(lián)操作

級聯(lián)的配置和一對多是一樣的,只需要在@ManyToMany配上cascade = CascadeType.ALL屬性。

    @ManyToMany(targetEntity = Language.class,cascade = CascadeType.ALL)
    @JoinTable(name = "middle_develop_language",
            joinColumns = @JoinColumn(name = "develop_id", referencedColumnName = "developerId"),
            inverseJoinColumns = @JoinColumn(name = "language_id", referencedColumnName = "languageId"))
    private List<Language> languages = new ArrayList<>();

級聯(lián)保存

Developer developer = new Developer();
developer.setName("lxf");
Language language = new Language();
language.setDes("java");

developer.getLanguages().add(language);

entityManager.persist(developer);

級聯(lián)刪除

Developer developer = entityManager.find(Developer.class, 1);
entityManager.remove(developer);//注意此時只能通過developer級聯(lián)刪除,因為只有Developer配置了cascade

對象導(dǎo)航查詢

對象導(dǎo)航查詢并不是一種新的查詢方式,而是在多表關(guān)系中,通過查詢某個對象,可以直接通過get得到相關(guān)聯(lián)的數(shù)據(jù)信息,這是jpa的一種特性,但需要注意:

  • jpa默認(rèn)使用的是懶加載的方式來獲取相關(guān)信息,即首次查詢時只查了單表信息,如果需要使用其相關(guān)信息,才會再次發(fā)送sql語句查詢數(shù)據(jù)庫。
  • 如果需要立即加載,即在一開始就直接查詢關(guān)聯(lián)表的所有信息,則需要在映射關(guān)系中配置fetch屬性。
    • FetchType.EAGER:立即加載
    • FetchType.LAZY:懶加載
  • fetch的默認(rèn)值:需要查多方的默認(rèn)懶加載,需要查單方的默認(rèn)立即加載
    • @OneToMany:默認(rèn)FetchType.LAZY
    • @ManyToMany:默認(rèn)FetchType.EAGER
    • @ManyToMany:默認(rèn)FetchType.LAZY
    @OneToMany(mappedBy = "school",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
    private List<Student> students = new ArrayList<>();

通過一方查多方

以上面的一對多案例為例,通過查詢一個學(xué)校,得到該學(xué)校的所有學(xué)生。

public class JpaObjectQueryTest {
    @Test
    public void test(){
        EntityManager entityManager = JPAUtil.getEntityManager();
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        try {
//            School school = entityManager.find(School.class, 5);
            School school = entityManager.getReference(School.class, 5);
            //可以直接通過get方法來得到和其相關(guān)的信息,如果懶加載,則此時會發(fā)送sql語句查詢關(guān)聯(lián)表信息
            List<Student> students = school.getStudents();
            System.out.println(students.size());
            students.forEach(new Consumer<Student>() {
                @Override
                public void accept(Student student) {
                    System.out.println(student);
                }
            });

            transaction.commit();
        }catch (Exception e){
            transaction.rollback();
            e.printStackTrace();
            throw new RuntimeException("出異常啦");
        }finally {
            entityManager.close();
        }
    }
}

通過多方查一方

            Student student = entityManager.find(Student.class, 5);
//            Student student = entityManager.getReference(Student.class, 5);
            School school = student.getSchool();
            System.out.println(school);

Demo源碼地址

https://github.com/lunxinfeng/jpa

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 什么是JPA Spring Data JPA入門 JPA(Java Persistence API)是Sun官方提...
    sherlockwit_孫鳴閱讀 496評論 0 2
  • 2017年8月21日 我原本只想簡單記錄一下springboot中應(yīng)用Jpa的簡單操作。不想由于hibernate...
    行者N閱讀 6,738評論 0 23
  • JPA是一種規(guī)范,而hibernate是JPA的一種實現(xiàn) JPA全稱為Java Persistence API ,...
    hangover_bfc9閱讀 766評論 0 0
  • 作為規(guī)范,Java Persistence API關(guān)注持久性,它將Java對象的創(chuàng)建過程和具體的創(chuàng)建形式解耦。并非...
    Java高級架構(gòu)獅閱讀 1,433評論 0 2
  • 今天本來是風(fēng)和日麗的一天,雖然上午的悶熱是那么令人不悅,但萬萬沒想到,午后的冰雹竟然闖進了大學(xué)城,砸得我們猝不及防...
    Mood_1f5b閱讀 495評論 0 0

友情鏈接更多精彩內(nèi)容