Java for Web學習筆記(一三二)對映(8)@ElementCollection
說明
在前面我們學習了OneToOne、OneToMany、ManyToOne,將資料庫中不同表格的關聯轉換為spring中不同entity的關聯。但是在不少場景中,我們希望在一個entity的檢視中同檢視到這幾個表的資訊,而無需通過entity之間的關聯。
在這個小例子中,我們還將學習和討論到:
- 表格有Employee,Employee_Address中存放員工多個地址,Employee_Phone中存放員工多個聯絡電話,Employee_Property中存放員工不確定的屬性。我們將通過一個entity(Employee)來獲取員工的資訊,並實現對員工資訊的增刪改查。
- FetchType.EAGER和FetchType.LAZY的差異,以及程式碼的注意事項。
- 從jsp頁面到spring framework到資料庫中中文資訊寫入如何避免亂碼的一些注意事項。(放在下一學習)
基礎類(String)存放在集合(有順序的List)
-- 這是一個基礎的員工資訊表,很簡單,姓名和Id CREATE TABLE `Employee` ( `EmployeeId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `FirstName` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `LastName` varchar(50) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`EmployeeId`) ) ENGINE=InnoDB -- 員工的電話號碼,一個員工可以有多個電話號碼,其中Protrity用於在電話列表中的優先排序。 CREATE TABLE `Employee_Phone` ( `Employee` bigint(20) unsigned NOT NULL, `Priority` smallint(5) unsigned NOT NULL, `Number` varchar(20) COLLATE utf8_unicode_ci NOT NULL, CONSTRAINT `Employee_Phone_Employee` FOREIGN KEY (`Employee`) REFERENCES `Employee` (`EmployeeId`) ON DELETE CASCADE ) ENGINE=InnoDB
@Entity
public class Employee {
private long id;
private String firstName;
private String lastName;
//【1】設定List屬性:將從Employee_Phone中獲取員工的電話資訊。
private List<String> phoneNumbers = new ArrayList<>();
//【2】從collection table中獲取獲取集合(本例子為List)的資訊。
// 2.1)@ElementCollection:在小例子中,頁面分頁顯示時給出員工的姓名,也給出聯絡電話,
// 因此在業務邏輯上獲取entity物件的同時,就需要獲取電話資訊,採用FetchType.EAGER。
// 2.2)@CollectionTable:給出關聯表格資訊。表格Employee_Phone的外來鍵Employee指向
// entity對應表格的EmployeeId列。
// 2.3)@ [email protected]:讀取CollectionTable中的Number列資訊,以Priority為排序,
// 存放到集合(List)中。表格中的priority或是List的index,即0,1,2,...
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable( name="Employee_Phone",
joinColumns = { @JoinColumn(name = "Employee", referencedColumnName = "EmployeeId")})
@OrderColumn(name="Priority")
@Column(name="Number")
public List<String> getPhoneNumbers() {
return phoneNumbers;
}
... ...
}
Embeddabel類作為集合(Set)
員工還有地址資訊,也是可以有多條。
CREATE TABLE `Employee_Address` (
`Employee` bigint(20) unsigned NOT NULL,
`Street` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`City` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`State` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`Country` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`PostalCode_Code` varchar(10) COLLATE utf8_unicode_ci NOT NULL,
`PostalCode_Suffix` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL,
CONSTRAINT `Employee_Address_Employee` FOREIGN KEY (`Employee`)
REFERENCES `Employee` (`EmployeeId`) ON DELETE CASCADE
) ENGINE=InnoDB
1)定義@Embeddable類
// 在javadoc中對@ElementCollection的解釋:
// Specifies a collection of instances of a basic type or embeddable class。
// 地址是個複雜的類,要能在entity中做為ElementCollection,這個類需要是Embeddable
@Embeddable
public class Address {
private String street;
private String city;
private String state;
private String country;
private PostalCode postalCode;
... ...
}
2)獲取@Embeddable類的集合,在entity中指定@Embeddable類屬性對應的列
@Entity
public class Employee {
//【1】假定這些資訊沒有先後順序,存放在集合Set中。
private Set<Address> addresses = new HashSet<>();
//【2】從collection table中獲取獲取集合(本例為Set)的資訊。
/* - @ElementCollection:在小例子中,頁面分頁顯示時給出員工的姓名,也給出聯絡電話,但並不提
供地址資訊,只有點員工詳細檢視時才提供。也就是隻有需要的時候才獲取Adress資訊,這種情況
採用FetchType.LAZY。
- @Embeddable類如果是entity的屬性,則必須在同一個表格(之前學習),如果是collection的
一個元素,則可以在不同的表格(現在學習),這個表的預設名字為entity表名_屬性名,本例為
Employee_Address,符合預設定義,安全地,我們仍在@CollectionTable中設定。
- @AttributeOverrides:本例子中列名和屬性名是一致的,如果不一致,可以通過
@AttributeOverrides來設定。當然也可以在@Embeddable類中屬性採用@Column來指定。但是如
果這個@Embeddable最終對映到兩個或者以上表,列名不一樣時,就需要在entity中指定。*/
@ElementCollection(fetch = FetchType.LAZY)
@CollectionTable(name="Employee_Address",
joinColumns ={@JoinColumn(name = "Employee", referencedColumnName = "EmployeeId")})
@AttributeOverrides({ @AttributeOverride(name="street",column = @Column(name="Street")),
@AttributeOverride(name = "city", column = @Column(name = "City")),
@AttributeOverride(name = "state", column = @Column(name = "State")),
@AttributeOverride(name = "country", [email protected](name = "Country")) })
public Set<Address> getAddresses() {
return addresses;
}
... ...
}
FetchType.LAZY
在這個例子中,Set<Address>採用了fetch = FetchType.LAZY,也就是程式碼中涉及該屬性處理時,才會去資料庫中獲取。我們有可能會碰到下面的異常:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role:
cn.wei.flowingflying.chapter24.entities.Employee.addresses, could not initialize proxy - no Session
我們如果要對entity進行處理,如果後續用使用到lazy的屬性,就沒有初始化,會報錯。例如,需要save entity,即使沒有對Adress的資訊進行改動,但在Spring data中也是對完整資訊進行處理。解決方式就是要確保整獲完整的entity的資料。我們在Service中增加一個public Employee getEmployee(long id) 介面來替代Spring data中的findOne介面,用於獲取完整的entity資訊。
@Inject EmployeeRepository employeeRepository;
@Override
//【1】加入到transactional中。這裡讀了兩次,第一次是普通讀(不獲取LAZY屬性);第二次是獲取LAZY屬
// 性。兩次操作有管理,放在一個事務中。
@Transactional
public Employee getEmployee(long id) {
Employee employee = this.employeeRepository.findOne(id);
//【2】實現初始化,例如呼叫employee.getAddresses()進行某個操作,例如.size(),觸發在資料庫中
// 獲取相關的資訊。
employee.getAddresses().size();
return employee;
}
對映到集合Map
給員工資料增加一個擴充套件屬性說明
CREATE TABLE `Employee_Property` (
`Employee` bigint(20) unsigned NOT NULL,
`KeyName` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`Value` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
CONSTRAINT `Employee_Property_Employee` FOREIGN KEY (`Employee`)
REFERENCES `Employee` (`EmployeeId`) ON DELETE CASCADE
) ENGINE=InnoDB;
@Entity
public class Employee {
//【1】這些擴充套件資訊放在key-value對中。
private Map<String, String> extraProperties = new HashMap<>();
/*【2】從collection table中獲取獲取鍵值對(本例為Map)的資訊。
@Column和@MapKeyColumn分別代表CollectionTable中的value所在列和key所在列。如果是列舉做為
值,則分別對應@Enumerated和@MapKeyEnumerated,如果是時間,則對應@Temporal和
@MapKeyTemporal。不僅僅可以使用基礎類,還可以通過convert轉換為其他型別,或者是embeddable類 */
@ElementCollection(fetch=FetchType.EAGER)
@CollectionTable(name="Employee_Property",
joinColumns ={@JoinColumn(name = "Employee", referencedColumnName = "EmployeeId")})
@Column(name = "Value")
@MapKeyColumn(name = "KeyName")
public Map<String, String> getExtraProperties() { ... }
... ...
}
相關推薦
Java for Web學習筆記(一三二)對映(8)@ElementCollection
說明 在前面我們學習了OneToOne、OneToMany、ManyToOne,將資料庫中不同表格的關聯轉換為spring中不同entity的關聯。但是在不少場景中,我們希望在一個entity的檢視中同檢視到這幾個表的資訊,而無需通過entity之間的關聯。 在這個小例子
Java for Web學習筆記(一四一)Spring security準備(2)授權
訪問的範圍和許可權屬於授權。 Principals和Identities 就Java而言,很方便利用java.security.Principal。Principal至少會包含已被認證的使用者identity,例如使用者名稱,還可能有其他資訊。此外還可以保護使用者的授權資
Java for Web學習筆記(一四二)Spring security準備(3)初窺
瞭解Spring Security的基本知識 完全J2EE的web container也能提供完整的安全框架,但tomcat不是。Spring Security可以使用JDBC,或者我們的服務或倉庫來認證使用者,也提供了內建的對微軟Active Derectory,Jasi
Java for Web學習筆記(十一):JSP(1)何為JSP
使用JSP 通java程式碼來編寫HTML很是繁瑣,通過使用JSTL(JSP標準標籤庫,JavaServer Pages™ Standard Tag Library),通過JSP來更便攜的實現。對JSP的支援Maven中包括javax.servlet.jsp-api、javax.servlet.jsp.js
Java for Web學習筆記(三五):自定義tag(3)TLDS和Tag Handler
JSTL的TLD 這是JSTL採用的方式。TLD(Tag Library Descriptor)描述tag和function,以及具體執行的java程式碼tag handler。Tag Handler是javax.servlet.jsp.tagext.Tag或javax.servlet.jsp.tage
Java for Web學習筆記(一二三):搜尋(5)MySQL全文索引(下)
小例子我們在表格Ticket和TicketComment中加入了fulltext key。小例子在Ticket的Subject或Body,以及在TicketComment的Body檢索內容,按分頁方式顯示出來,同時顯示關聯分數,並按關聯分數降序排列。 -- Ticket中隊S
Java for Web學習筆記(一):Java EE的介紹
最近有個專案,當中涉及到Java Servlet,專案已經進行了一段日子,最近因為有重大需求調整,需要對架構重新進行梳理,一看,基本上哭笑不得,有不少地方真是黑色幽默,怎麼會這樣呢。 當在這過程中,也引發了我興趣,畢竟之前沒有系統地瞭解過。在網上找到了一本書professional Java for
Java for Web學習筆記(一一一):再談Entity對映(4)動態表格建立
如果這個不確定表格也需要我們的war來建立,如何實現。create table的原生SQL,entityManager是無法執行的,因為這不是可以回滾的事務。這種情況,我們需要: 捕獲表格不存在的異常 從原始的Connection中實現表格建立。獲取Connection能否從
Java for Web學習筆記(一三八)篇外之資料庫的ACID和JPA(2)JPA
在測試中,我們發現在一個JPA事務中: Spring Data的寫SQL是在最後commit前發出,這最大程度地縮短了寫操作和commit之間的時間。 對相同的ID的讀,JPA只從資料庫中讀取一次。 從資料庫中獲取entity,修改entity的資料,即使最後沒有執行s
Java for Web學習筆記(二五):JSTL(1)使用JSTL
在前面已經使用過JSTL,例如<c:url>,fn是JSTL的functionlibrary,而c是JSTL的tag library。使用它們,我們要告知解析器,如下: <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core
Java for Web學習筆記(十九):Session(3)Session Listener
Session Listener 可以通過Listner來監聽session的變化,這就是所謂的publish and subscribe模型。這是一種訊息資訊釋出一方叫釋出者,資訊的接收方叫訂閱者,實際也是事件驅動的高大上說法,訂閱某個事件,然後觸發處理。這種方式最大的作用是將進行session變化以及s
Java for Web學習筆記(八):Servlet(6)doGet()和doPost()是執行緒還是佇列
做一個小實驗,程式碼如下: protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try{ for(i
Java for Web學習筆記(九十):訊息和叢集(5)利用websocket實現訂閱和釋出(上)
叢集中的訂閱和釋出 利用spring framework在本app內的訂閱和釋出十分簡單。當我們系統越來越複雜的時候,我們需要向其他app釋出訊息。本學習將給出一個通過websocket來實現不同app之間訊息的訂購和釋出。 在小例子中,我們在所有節點之間都建立webSoc
Java for Web學習筆記(九):Servlet(7)上傳檔案
上傳檔案 Servlet的引數設定 採用annotation方式如下: @WebServlet( name = "TicketServlet", urlPatterns = {"/tickets"}, loadOnStartup = 1 ) /* MultipartConfig配置了本Servlet
Java for Web學習筆記(五十):Log(2)配置log4j2
相關maven <!-- log4j-api提供logging的API。這是log4j2中唯一需要在compile時加入,含有你所需的所有類 --> <dependency> <groupId>org.apache.loggin
Java for Web學習筆記(三九):Filter(1)用途、定義和順序
Filter的用途 Filter可以用於以下方面: 記錄request和response的log進行認證和授權進行壓縮和加壓,非HTTPS的加密和解密錯誤處理。對於tomcat,出現錯誤通常會給出一個500的頁面,還有錯誤診斷資訊,對於一個公眾服務,這些診斷資訊可能會向黑客
Java for Web學習筆記(七):Servlet(5)初始化引數
Context Init引數 在web.xml中可以基於整個上下文進行設定,在web app內各個Servlet都可以獲取。 <context-param> <param-name>settingOne</param-name> <param-
Java for Web學習筆記(四):Servlet(2)HelloServlet
繼承關係: javax.servlet.GenericServlet –》javax.servlet.http.HttpServlet。 405返回 如果我們不重寫Servlet的doGet而採用HTTP GET的方式,將返回405 將返回405 Method Not Allowed。 如果我們重寫do
java之jvm學習筆記十一(訪問控制器)
這一節,我們要學習的是訪問控制器,在閱讀本節之前,如果沒有前面幾節的基礎,對你來說可能會比較困難! 知識回顧: 我們先來回顧一下前幾節的內容,在筆記三的時候我們學了類裝載器,它主
Linux學習筆記第八周二次課(3月27日)
rsync -av rsync -av -e ssh -p 22 10.28 rsync工具介紹文件同步工具rsync,可以實現增量拷貝;cp是直接覆蓋,效率低;安裝rsync,#yum -y install rsync;rsync常用方法,拷貝passwd文件到/tmp目錄下,並重命名為1.tx