Jpa映射詳解
該博客例子均用 SpringBoot + Spring Data Jpa 實現
一、常用注解
這里主要介紹了最常用注解,實現POJO和數據庫的隱射。
@Entity
對類注釋。任何Hibernate映射對象都要有這個注釋
持久層將對象映射到數據庫,JPA是一種規范,Hibernate是一種實現,可以將POJO映射到數據庫。這種類就叫Entity Bean
@Table
對類注釋。通過它可以為實體指定表(talbe),目錄(Catalog)和schema的名字
@ID
聲明該屬性為主鍵,指定類的主鍵
@GeneratedValue
指定主建的聲明策略,自動生成主鍵
- TABLE:數據庫產生主鍵
- IDENTITY:數據庫自增長
- SEQUENCR :通過數據庫的序列產生主鍵
- AUTO:默認選項
@Column
聲明該屬性與數據庫字段的映射關系,Hibernate會自動匹配屬性和字段名字相同的,@Column可以修改隱射值
@Transitent
若屬性非數據庫字段,則可以聲明該屬性與數據庫字段的不產生映射關系
參考用例
# 配置 Hibernate 自動生成表
spring.jpa.properties.hibernate.hbm2ddl.auto=update
定義兩個實體表,Author和Poetry,就將剛才所有的注解配置上,啟動服務springboot 掃描注解@Entity后,會自動生成數據庫表,最后我們查看下數據庫表中的結構就知道答案了
Author
@Entity
@Table(name = "authorCN")
public class Author implements Serializable {
private static final long serialVersionUID = 4074367019988114836L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "p_name")
private String name;
@Column(name = "p_age")
private Integer age;
@Transient
private String error;
// getter and setter
}
Poetry
@Entity
@Table
public class Poetry implements Serializable {
private static final long serialVersionUID = -2743230230981482358L;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;
private String title;
private String summary;
private String content;
// getter and setter
}

查看數據庫表,可以看到Poetry的字段,表明都是和屬性一樣的,而Author里面加了@Column注解的屬性后,生成的字段名發生的改變,並且表名也有改變,@Transient下的屬性並沒有生成數據庫表中的字段。
通過測試啟動服務插入數據,我們可以查看主鍵的生成策略
@Test
public void testSavaData() {
authorRepository.save(new Author("杜甫", 56));
authorRepository.save(new Author("李白", 42));
poetryRepository.save(new Poetry("八陣圖", "詠懷詩", "功蓋三分國,名高八陣圖。江流石不轉,遺恨失吞吳。"));
poetryRepository.save(new Poetry("春望", "愛國詩", "國破山河在,城春草木深。感時花濺淚,恨別鳥驚心。烽火連三月,家書抵萬金。白頭搔更短,渾欲不勝簪。"));
poetryRepository.save(new Poetry("靜夜思", "思鄉詩", "床前明月光,疑是地上霜。舉頭望明月,低頭思故鄉"));
}
我們查看日志可以得知,因為在Poetry里面設置的生成策略是SEQUENCE,所以,在每次插入數據的時候,都有hibernate_sequence的更新。
Hibernate: insert into authorcn (p_age, p_name) values (?, ?)
Hibernate: insert into authorcn (p_age, p_name) values (?, ?)
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into poetry (content, summary, title, id) values (?, ?, ?, ?)
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into poetry (content, summary, title, id) values (?, ?, ?, ?)
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into poetry (content, summary, title, id) values (?, ?, ?, ?)
二、POJO之間的關系
(1)一對一關系
因為之前古詩和作者是多對一關系,所以呢,這里我要將一對一,我就創建了妻子的表,夫妻關系一對一,哈哈,古代我們也是一夫一妻制
在這之前,我偷偷的在數據庫加了些測試數據,這個就不列出來了
@Entity
@Table
public class Wife implements Serializable {
private static final long serialVersionUID = 381807136517697285L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String wname;
private Integer wage;
// getter and setter
在Author聲明屬性wife,指定對應關系
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "wife_id")
private Wife wife;
@OneToOne
顧名思義,就是一對一關系,cascade是級聯操作,CascadeType有多種配置
- ALL 所有
- PERSIST 級聯持久化(保存)操作
- MERGE 級聯更新(合並)操作
- REMOVE 移除
- REFRESH 刷新
- DETACH 脫管/游離操作
@JoinColumn
保存表與表之間關系的字段,同@Column,name只指定數據庫字段,wife_id,但是默認指向該表的主鍵,如何要讓wife表其他字段做外建,就需要使用referencedColumnName指明對應表的字段。我這里沒有明確舉例,其他小伙伴可以自己去嘗試。
@JoinColumn(name = "wife_id",referencedColumnName="name")
測試:
建立Controller類,啟動服務訪問
@RestController
@RequestMapping("/jpa")
public class IndexController {
@Autowired
AuthorRepository authorRepository;
@Autowired
PoetryRepository poetryRepository;
@Autowired
WifeRepository wifeRepository;
@GetMapping("/author/{id}")
public Author findAuthorById(@PathVariable(value = "id") Integer id){
return authorRepository.findById(id).get();
}
}
這里@Controller
效果如圖:

當然這里只是一對一的單向關聯,主導權全在author那邊,而wife這邊沒有任何可以修改的權限,對於這種也是可以有解決的辦法的,我們建立雙向關聯,
在wife類里添加
@OneToOne(mappedBy="wife")
@JsonBackReference
private Author author;
mappedBy指的被映射,如何要雙向關聯,必須要指定mappedBy,
而@JsonBackReference注解是因為雙向關聯后,你中有我,我中有你,就會無限循環下去,所以在任何一方添加 @JsonBackReference可以解決這個問題,通過debug我們可以看到如果不加 @JsonBackReference的后果

(2)一對多關系
這里一對多關系,我們就用之前的作者表(author)和古詩(Poetry),李白一個人就有一千多首詩呢
好了,還是先單向關聯,主動權肯定在author 作者這邊。
在Author加入
@OneToMany
private List<Poetry> poetryList;
這里就有點不同了,一對一中外鍵關系wife_id主動權在author手中,那么數據表這邊就會添加一個字段wife_id,
但是一對多就不同了,主鍵但是對應多個,不可能生成外鍵,
所以外鍵是放在多那邊,就是古詩表(poetry);
Poetry
多對一里面是主動的,@JoinColumn指定外鍵authorCN_id
@ManyToOne
@JoinColumn(name = "authorCN_id")
@JsonBackReference
private Author author;
Author
一對多里面是被動的,mappedBy指向Poetry里面的author
@OneToMany(mappedBy = "author")
private List<Poetry> poetryList;
建立后,查詢結果如下,可以看到關聯到杜甫的詩就都出來了

(3)多對多關系
多對多的話,就拿讀者和古詩舉例吧,讀者可以讀很多古詩,古詩也可以有不同的讀者
@Entity
@Table
public class Reader implements Serializable {
private static final long serialVersionUID = 4074367019988114836L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private Integer age;
@ManyToMany
@JoinTable(name = "PoetryAndReader", joinColumns ={@JoinColumn(name = "reader_id")},
inverseJoinColumns = {@JoinColumn(name = "poetry_id")})
private List<Poetry> poetryList;
}
@ManyToMany
多對多的注解
@JoinTable
因為多對多不存在直接外鍵外鍵,所以建立第三方表里面存儲雙方法的主鍵作為外鍵,name就是第三方表的名字,joinColumns就是配置連接表中外鍵列的信息,該屬性值可接受多個@JoinColumn,用於配置連接表中外鍵列的信息

結束
里面的代碼只粘貼了重要部分的,如果哪個小伙伴要跑一下看下具體效果,我把代碼貼到了GitHub里面,有興趣的可以去copy.
