JPA提供了一種簡單高效的方式來管理Java對象(POJO)到關係數據庫的映射,此類Java對象稱爲JPA實體或簡稱實體。實體一般與底層數據庫中的單個關係表相關聯,每一個實體的實例表示數據庫表格中的某一行。java
實體管理器(EntityManager)用於管理系統中的實體,它是實體與數據庫之間的橋樑,經過調用實體管理器的相關方法能夠把實體持久化到數據庫中,同時也能夠把數據庫中的記錄打包成實體對象。數據庫
在此以前咱們要先了解實體的狀態及其轉換,見下圖數組
JPA實體生命週期有四種狀態app
狀態名 | 做爲java對象存在 | 在實體管理器中存在 | 在數據庫存在 |
New | Y | N | N |
Managed | Y | Y | Y |
Datached | N | N | N |
Removed | Y | Y | N |
用一段程序來示範ide
@Transactional public void save(){ //New 狀態 Task t = new Task(); t.setTaskName("task" + new Date().getTime()); t.setCreateTime(new Date()); //Managed狀態 em.persist(t); //實體類t已經有id t.getId(); t.setTaskName("kkk"); //更新任務名稱,這時,若是提交事務,則直接將kkk更新到數據庫 //Detached狀態 事務提交或者調用em.clear都直接將實體任務狀態變爲Detached em.clear(); t.setTaskName("kkk"); //更新數據不會更新到數據庫 //Removed狀態 em.remove(t); }
對應於實體的四種狀態,實體管理器有四種經常使用的方法,分別是:persist / merge / clear / remove,結合狀態圖,能夠判斷,對於不一樣狀態下的實體,各個方法操做結果會有不一樣:fetch
對於不一樣狀態下的實體,persist 操做結果以下:ui
對於不一樣狀態下的實體,merge 操做結果以下:spa
對於不一樣狀態下的實體,refresh 操做結果以下:hibernate
對於不一樣狀態下的實體,remove 操做結果以下:code
public class UserRepositoryImpl { @PersistenceContext private EntityManager entityManager; @Transactional public void add(User user) { entityManager.persist(user); } @Transactional public User update(User user) { User userUpdate = entityManager.find(User.class, user.getId()); userUpdate.setAddress(user.getAddress()); userUpdate.setName(user.getName()); userUpdate.setPhone(user.getPhone()); return userUpdate; } @Transactional public User addOrUpdate(User user) { return entityManager.merge(user); } @Transactional public void delete(User user) { entityManager.remove(user); } public User findOne(Integer id) { return entityManager.find(User.class, id); } public List<User> findAll() { String queryString = "select u from User u"; Query query = entityManager.createQuery(queryString); return query.getResultList(); } }
@Entity //表示該類爲JPA實體類 @Table(name="t_user") //對應數據庫中哪張表 public class User { @Column(name="phone_", length=11, nullable=true,columnDefinition="CHAR(10) default '000'") //對應數據庫表中哪一個列字段及對該字段的自定義 private String phone;
@Id //標明主鍵 @GeneratedValue //主鍵生成策略 @Column(name="id_") private Integer id;
更多的主鍵生成策略,詳見3.6 的整體代碼
@Column(name="phone_", length=11, nullable=true,columnDefinition="CHAR(10) default '000'") //對應數據庫中哪一個列及對該字段的自定義 private String phone;
一般一個實體對應於一個表格,即表格中的全部的實體屬性都存放於一張表,若是將實體的屬性分配到多個表格存放,就涉及到單實體多表格存儲
@Entity @Table(name="t_user",catalog="",schema="") @SecondaryTables({ //指明存放的第二張表 @SecondaryTable(name = "t_address",pkJoinColumns=@PrimaryKeyJoinColumn(name="address_id")) }) public class User { @Column(name="name_", length=60, nullable=false,unique=true,insertable=false) private String name; //分表存儲 @Column(table = "t_address", name="street_", length = 100) private String street;
在定義實體時可能須要將某幾個的屬性剝離出放到另一個實體中,以使程序更有層次感,而且當其餘實體也須要這幾個屬性時,咱們也不須要再定義這幾個屬性,把存放這幾個屬性的實體從新引用便可,操做方法以下:
@Embeddable //標識該實體可嵌入到其餘實體中 public class Comment { @Column(name="title_",length=100) String title; @Column(name="content_") String content;
/* //被剝離出的屬性 @Column(name="title_",length=100) String title; @Column(name="content_") String content; */ @Embedded //引入該實體 @AttributeOverrides({ //羅列出全部須要從新命名的屬性 @AttributeOverride(name = "title", column = @Column(name = "user_title")), @AttributeOverride(name = "content", column = @Column(name = "user_content")) }) private Comment comment;
內嵌實體在數據庫中不會一點單獨的表格存放,而是跟數組實體存放於同一表格中。
import java.math.BigDecimal; import java.util.Date; import javax.persistence.*; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name="t_user",catalog="",schema="") @SecondaryTables({ @SecondaryTable(name = "t_address",pkJoinColumns=@PrimaryKeyJoinColumn(name="address_id")) }) public class User { @Id //標明主鍵 @GeneratedValue //主鍵生成策略 @Column(name="id_") private Integer id; /* @Id @GeneratedValue(generator="uuidGenerator") @GenericGenerator(name="uuidGenerator",strategy="uuid") @Column(name="id_",length=32) private String id;*/ /* @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name="id_") private Integer id;*/ /* * @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="id_") private Integer id;*/ /* @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "idGenerator") @SequenceGenerator(name = "idGenerator",sequenceName="mySeq",allocationSize=1) @Column(name="id_") private Integer id;*/ /* @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "userGenerator") @TableGenerator(name = "userGenerator",table="pk_generator", pkColumnName="gen_name", valueColumnName="gen_value", pkColumnValue="user_pk", initialValue=0, allocationSize=1) @Column(name="id_") private Integer id; */ @Column(name="name_", length=60, nullable=false,unique=true,insertable=false) private String name; @Column(name="address_", length=60, nullable=false) private String address; @Column(name="phone_", length=11, nullable=true,columnDefinition="CHAR(10) default '000'") private String phone; @Column(name="inCome_", precision=12, scale=2) private BigDecimal inCome; @Temporal(TemporalType.DATE) private Date birthday; //Date 日期型,精確到年月日,例如「2008-08-08」 //Time 時間型,精確到時分秒,例如「20:00:00」 //Timestamp 時間戳,精確到納秒,例如「2008-08-08 20:00:00.000000001」 @Lob @Column(name="pic_") @Basic(fetch=FetchType.LAZY) private byte[] pic; @Lob @Column(name="note_") @Basic(fetch=FetchType.LAZY) private String note; //分表存儲 @Column(table = "t_address", name="street_", length = 100) private String street; //分表存儲 @Column(table = "t_address", name="city_") private String city; //分表存儲 @Column(table = "t_address", name="conutry_",length = 20) private String conutry; @Column(name="title_",length=100) String title; @Column(name="content_") String content; /* @Embedded //引入該實體 @AttributeOverrides({ //羅列出全部須要從新命名的屬性 @AttributeOverride(name = "title", column = @Column(name = "user_title")), @AttributeOverride(name = "content", column = @Column(name = "user_content")) }) private Comment comment; */ //省略get/set方法 }
以下例,人員表(person)和地址表(adddress),person表是關係的擁有者,表中的address_id字段關聯着address表的主鍵id。
@Entity public class Person { //略
@OneToOne @JoinColumn(name="address_id",referencedColumnName="aid")//name:主表的外鍵字段; referencedColumnName:從表的主鍵 //若是關聯的字段有多個,採用以下註解 //@JoinColumns(value={@JoinColumn(name="address_id",referencedColumnName="aid"),@JoinColumn(name="address_id2",referencedColumnName="aid2")}) private Address address;
部門表(depart)和員工表(employee),一個部門能夠有多個員工,一對多關係能夠採用以下兩種實現方法。
建立中間表(depart_employee),表中存放兩個表的主鍵。經過部門id可查詢關聯員工的id,三張表存在兩個主外鍵關係。
@Entity public class Depart { //略 @OneToMany @JoinTable(name = "depart_employee", //name:關聯表 joinColumns = @JoinColumn(name = "depart_id",referencedColumnName="did"), //joinColumns:關係的擁有者與關聯表的關係 inverseJoinColumns = @JoinColumn(name = "employee_id",referencedColumnName="eid"))//inverseJoinColumns:關係的被擁有者與關聯表的關係 private List<Employee> employees;
在員工表(employee2)中添加一個depart_id字段,它做爲外鍵關聯部門表(depart2)的主鍵id。
@Entity public class Depart2 { //略 @OneToMany @JoinColumn(name="depart_id",referencedColumnName="id") private List<Employee2> employee2s;
多對多的實現也是經過中間表,方法同一對多的中間表實現方式。
@Entity public class Teacher { //略 @ManyToMany @JoinTable(name = "teacher_student", joinColumns = @JoinColumn(name = "teacher_id",referencedColumnName="tid"), inverseJoinColumns = @JoinColumn(name = "student_id",referencedColumnName="sid")) private List<Student> students;
@Entity public class Student { //略 @ManyToMany(mappedBy = "students") private List<Teacher> teachers;
以@OneToOne爲例,當我但願刪除人員信息時,也將其地址信息刪除,則可以使用級聯策略;當我想要查詢人員信息(主實體)時,並不想同時查詢出其地址信息(子實體),能夠設置懶加載。
@Entity public class Person { @OneToOne(cascade={CascadeType.REFRESH,CascadeType.REMOVE},fetch=FetchType.LAZY) //@JoinColumn(name="address_id",referencedColumnName="aid") private Address address;
繼承[extends]想必已不陌生,對於JPA來講,咱們不但要考慮如何實現Java端的繼承關係,還要考慮如何持久化到數據庫中。JPA爲此提供了三種策略,以下:
繼承關係如圖,繼承策略的註解主要應用於父類Item。
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) public class Item {
執行單一表策略會將全部實體的信息存放於一張表中,它的優勢是信息存放於一張表,查詢效率較高,缺點是大量字段爲空,浪費存儲空間。
若是類名過長或須要更改鑑別字段的名稱,可對鑑別字段及可選值自定義:
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="ITYPE",discriminatorType=discriminatorType.CHAR) //聲明鑑別字段的字段名,類型 @DiscriminatorValue("I") //該表在鑑別字段列顯示的值 public class Item {
@Entity @DiscriminatorValue("P") public class Phone extends Item {
@Entity @DiscriminatorValue("B") public class Book extends Item {
效果以下
@Entity @Inheritance(strategy = InheritanceType.JOINED) public class Item {
鏈接表策略會生成三張表,經過共享主鍵彼此關聯。
這種策略避免了空字段的浪費,但因爲採用表關聯查詢,當數據量過大時,查詢效率較低。
@Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public class Item { /* @Id @GeneratedValue(strategy = GenerationType.AUTO) */ @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "ItemGenerator") @TableGenerator(name = "ItemGenerator",table="pk_generator", pkColumnName="gen_name", valueColumnName="gen_value", pkColumnValue="item_pk", initialValue=0, allocationSize=1) private Long id;
每一個類策略其實是每一個類一個表策略,這種策略要求主鍵不能使用自增的方式,如上面的代碼,採用表中獲取的方式。
三張表各自存放本身的完整信息,表之間沒有任何的關聯關係。雖然他們各自存放各自的數據,但主鍵是連續的。即三個表共用一套主鍵生成策略(三個表的主鍵都從另外一個表中獲取)。
這種策略查詢效率高,同時也不存在大量空字段的浪費。