1. 程式人生 > >Java for Web學習筆記(一三二)對映(8)@ElementCollection

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學習筆記:JSP1何為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學習筆記:自定義tag3TLDS和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學習筆記一二:搜尋5MySQL全文索引

小例子我們在表格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和JPA2JPA

在測試中,我們發現在一個JPA事務中: Spring Data的寫SQL是在最後commit前發出,這最大程度地縮短了寫操作和commit之間的時間。 對相同的ID的讀,JPA只從資料庫中讀取一次。 從資料庫中獲取entity,修改entity的資料,即使最後沒有執行s

Java for Web學習筆記:JSTL1使用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學習筆記十九:Session3Session Listener

Session Listener 可以通過Listner來監聽session的變化,這就是所謂的publish and subscribe模型。這是一種訊息資訊釋出一方叫釋出者,資訊的接收方叫訂閱者,實際也是事件驅動的高大上說法,訂閱某個事件,然後觸發處理。這種方式最大的作用是將進行session變化以及s

Java for Web學習筆記:Servlet6doGet()和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學習筆記:Servlet7上傳檔案

上傳檔案 Servlet的引數設定 採用annotation方式如下: @WebServlet( name = "TicketServlet", urlPatterns = {"/tickets"}, loadOnStartup = 1 ) /* MultipartConfig配置了本Servlet

Java for Web學習筆記五十:Log2配置log4j2

相關maven <!-- log4j-api提供logging的API。這是log4j2中唯一需要在compile時加入,含有你所需的所有類 --> <dependency> <groupId>org.apache.loggin

Java for Web學習筆記三九:Filter1用途、定義和順序

Filter的用途 Filter可以用於以下方面: 記錄request和response的log進行認證和授權進行壓縮和加壓,非HTTPS的加密和解密錯誤處理。對於tomcat,出現錯誤通常會給出一個500的頁面,還有錯誤診斷資訊,對於一個公眾服務,這些診斷資訊可能會向黑客

Java for Web學習筆記:Servlet5初始化引數

Context Init引數 在web.xml中可以基於整個上下文進行設定,在web app內各個Servlet都可以獲取。 <context-param> <param-name>settingOne</param-name> <param-

Java for Web學習筆記:Servlet2HelloServlet

繼承關係: 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