用了一段時間的Spring,到現在也只是處於會用的狀態,對於深入一點的東西都不太了解。所以決定開始深入學習Spring。
本文主要記錄JPA學習。在學習JPA之前,需要了解一些ORM的概念。
ORM概念:
對象關系映射ORM(Object/Relation Mapping)是一種為了解決面向對象和關系數據之間存在互不匹配現象的技術(范式不匹配)。簡而言之,ORM是通過使用描述對象和數據庫之間映射的元數據,將程序中的對象自動持久化到數據庫中。本質上是將數據從一種形式轉換到另外一種形式。(個人考慮:但是這麼做,感覺好像和一般方法比起來會有額外的開銷吧?待解決)
范式不匹配:
ORM具備功能:
一般ORM框架包括以下四個功能:
映射發生情況:
對象-關系映射一般發生在以下情況:
以上就是JPA的一些概念,接下來上JPA。
JPA功能:
JPA(Java persistence API)主要用用於Java中處理持久化操作。對ORM特性和功能進行標准化。分別定義了用來將對象模型映射到關系模型的API、可以在對象上執行CRUD操作、一種對象查詢語言以及通過對象圖獲取數據的標准API。
JPA好處:???都還沒怎麼用,沒法做出比較。還是先使用一段時間後,再來評價
使用JPA:
定義實體:
實體定義:一個對象在數據庫端與一個帶有主鍵的記錄相對應,那麼該對象就稱為實體。
下面代碼定義了一個最簡單的實體類:
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; /** * * <p>ClassName: User.java<p> * <p>用戶實體類<p> * @author linzj * @createTime 2016年3月17日 下午1:08:35 */ @Entity @Table(name="user") public class User { @Id @GeneratedValue private Long id; }
JPA為映射提供了java注解,可以發現這些注解都在javax.persistence包下。上面的@Entity注解定義了User類為持久化類型,並有一個對應的表。@Table注解指定了表名,如果沒有指定表明則默認使用類名為其表名。
@Id注解標記了主鍵屬性。如果主鍵列名與屬性名不匹配,則可以使用@Column來指定對應的主鍵列名,如添加上@Column(name=”uid”),就與uid列表對應。應用程序並不負責生成主鍵值,而是在新記錄插入期間由JPA供應商分配(如Hibernate)。通過@GenerateValue注解告訴JPA,應用程序將不會負責分配主鍵值,而是有JPA供應商處理。
屬性映射到列:
為了更加直觀的理解。我先建了一張User表:
mysql> create table user( -> id int(5) not null primary key auto_increment, -> username varchar(60) not null, -> passwd varchar(60) not null); Query OK, 0 rows affected (0.14 sec) mysql> describe user; +----------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+---------+----------------+ | id | int(5) | NO | PRI | NULL | auto_increment | | username | varchar(60) | NO | | NULL | | | passwd | varchar(60) | NO | | NULL | | +----------+-------------+------+-----+---------+----------------+
接下來我們就可以將剛剛的User實體類添加對應的屬性,並將屬性映射到上面這張user表中對應的列了:
/** * * <p>ClassName: User.java<p> * <p>用戶實體類<p> * @author linzj * @createTime 2016年3月17日 下午1:08:35 */ @Entity @Table(name="user") public class User { @Id @GeneratedValue private Long id; private String username; @Column(name = "passwd") private String password; @Transient private String others; }
因為表中沒有password這個字段,所以我們必須指定映射的列名,即指定password映射到passwd。我們還發現實體類中多了一個表裡沒有的屬性others,注意這個others屬性已經注解為@Transient,因此others屬性將被JPA忽略,不會進行映射。
對象之間創建關聯:
數據庫關聯類型有:
一對一關聯:
/** * * <p>ClassName: User.java<p> * <p> * 用戶實體類,通過@OneToOne注解標識一對一關聯, * @JoinColumn注解更加User和Adress實體指定了 * 表之間的外鍵關系。用戶表中的列引用了地址表 * <p> * @author linzj * @createTime 2016年3月17日 下午1:08:35 */ @Entity @Table(name="user") public class User { @Id @GeneratedValue private Long id; private String username; @Column(name = "passwd") private String password; @OneToOne @JoinColumn(name = "adress_id")//添加外鍵 private Adress adress; } /** * * <p>ClassName: Adress.java<p> * <p>地址實體類<p> * @author linzj * @createTime 2016年3月17日 下午1:52:30 */ @Entity public class Adress { //...pass }
多對一:
/** * * <p>ClassName: Employe.java<p> * <p> * 雇員實體類,使用了@ManyToOne注解,並使用了@JoinColumn指定兩表之間的外鍵關系, * optional=false 標識關聯是Employe必須的,即:Employe不能單獨存在。 * <p> * @author linzj * @createTime 2016年3月17日 下午1:55:11 */ @Entity public class Employe { //...pass @ManyToOne(optional = false) @JoinColumn(name = "company_id") private Company company; } @Entity public class Company { //...pass }
多對一:
/** * * <p>ClassName: Student.java<p> * <p> * 學生實體類,@OneToMany注解在學生和書本之間創建了一對多的關系, * 用@JoinColumn指定外鍵,不同的是這次外鍵列存在於Book實體中。 * <p> * @author linzj * @createTime 2016年3月17日 下午2:01:23 */ @Entity public class Student { //...pass @OneToMany @JoinColumn(name = "student_id") private Set<Book> books = new HashSet<Book>(); } @Entity public class Book { //...pass }
多對多:
/** * * <p>ClassName: Product.java<p> * <p> * 產品實體類,@ManyToMany創建多對多關聯, * 注意這裡用的是@JoinTable而不是@JoinColumn, * 因為一邊的多個實例可以與另一邊的多個實例相關聯。因此無法再一個列中保存指向另一個表的關聯數據。 * 所以這裡就需要提供一個關聯表,該表包含每個表的主鍵列引用。 * 本示例中,關聯表是product_catalog,joinColumns和inverseJoinColumns指定 * product_catalog表中的列名,joinColumns指定Product表主鍵的引用,inverseJoinColumns * 指定Catalog表主鍵的引用。 * <p> * @author linzj * @createTime 2016年3月17日 下午2:07:47 */ @Entity public class Product { @Id @GeneratedValue private Long id; @ManyToMany(cascade=CascadeType.ALL) @JoinTable(name = "product_catalog", joinColumns = @JoinColumn(name = "product_id"), inverseJoinColumns = @JoinColumn(name = "catalog_id")) private Set<Catalog> catalog = new HashSet<Catalog>(); public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Set<Catalog> getCatalog() { return catalog; } public void setCatalog(Set<Catalog> catalog) { this.catalog = catalog; } } @Entity public class Catalog { @Id @GeneratedValue private Long id; } /** 對應的表如下: mysql> describe product_catalog; +------------+------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +------------+------------+------+-----+---------+-------+ | product_id | bigint(20) | NO | PRI | NULL | | | catalog_id | bigint(20) | NO | PRI | NULL | | +------------+------------+------+-----+---------+-------+ 2 rows in set (0.01 sec) mysql> select * from product_catalog; +------------+------------+ | product_id | catalog_id | +------------+------------+ | 1 | 1 | | 1 | 2 | +------------+------------+ 2 rows in set (0.00 sec) mysql> select * from product; +----+ | id | +----+ | 1 | +----+ 1 row in set (0.00 sec) mysql> select * from catalog; +----+ | id | +----+ | 1 | | 2 | +----+ */
關聯的方向性:
關聯的方向性只有兩種:單向和雙向。
在單向關聯中,只能從關聯的源對象到目標對象。雙向關聯則處理能從關聯的源對象到目標對象外,還可以從目標對象到源對象。
在如下代碼中,我們只是將上面的Adress實體類修改了下,就構成了雙向關聯。我們在Adress實體類代碼中,添加了@OneToOne(mappedBy = "adress"),並且添加了mappedBy屬性,通過該屬性,JPA可以識別該屬性,從而管理兩個實體之間的關聯。使用mappedBy屬性的一邊可以看作成一個鏡子,或者是只讀的。它並不會對數據庫的數據產生影響。拿下面的代碼說明就是,用下面的實體類做持久化操作後,數據庫的Adress表裡並不會多一列關於user的字段。
@Entity @Table(name="user") public class User { @Id @GeneratedValue private Long id; private String username; @Column(name = "passwd") private String password; @OneToOne @JoinColumn(name = "adress_id")//添加外鍵 private Adress adress; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Adress getAdress() { return adress; } public void setAdress(Adress adress) { this.adress = adress; } } @Entity public class Adress { @Id @GeneratedValue private Long id; @OneToOne(mappedBy = "adress") private User user; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
上面代碼放在我的GITHUB
另外提供一些關於JPA的博客:
http://www.cnblogs.com/holbrook/archive/2012/12/30/2839842.html
http://www.cnblogs.com/chenying99/p/3143516.html
以上是本人學習JPA的記錄,下一篇介紹配置和使用JPA。