用了一段時間的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。