[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ù)庫信息、緩存、實體管理器對象等很多信息
- Persistence:用于創(chuàng)建實體管理器工廠(EntityManagerFactory)
- 獲取實體管理器
- EntityManager:實體管理器。
- getTransaction:創(chuàng)建事務(wù)對象
- persist:保存
- merge:更新
- remove:刪除
- find/getRefrence:根據(jù)id查詢
- EntityManager:實體管理器。
- 獲取事務(wù)對象,開啟事務(wù)
- EntityTransaction:事務(wù)對象。
- begin:開啟事務(wù)
- commit:提交事務(wù)
- rollback:回滾事務(wù)
- EntityTransaction:事務(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:參照的主表主鍵字段名稱
- @OneToMany:聲明一對多
@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ù)組,表示==對方對象==在中間表的外鍵
- @ManyToMany:聲明多對多
@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);