Java for Web學習筆記(九九):持久化初探(4)JPA小例子(下)__Java

來源:互聯網
上載者:User
Entity的映射

雖然在前面,我們給出了表格。但書推薦我們先設計代碼,然後根據代碼來設計資料庫,使用者需求-》代碼設計-》資料庫設計。但我覺得大項目可能不會這樣。 類和資料的映射

//表明這是 @javax.persistence.Entity,預設Entity的名字是類名,如果需要特指,可通過用name參數。@Entity(name = "PublisherEntity")/*可以不設定,預設的表名字就是entity的名字,本例如果不設定,則為PublisherEntity,現表名為Publishers。如果我們允許建立表格(注意,這是危險的,在生產環境中應當禁止)。我們可以在此設定主鍵外的其他key,例如: * @Table(name = "Books",  //表名 *        uniqueConstraints = { @UniqueConstraint(name = "Books_ISBNs", columnNames = { "isbn" })}, // unique key *        indexes = { @Index(name = "Books_Titles", columnList = "title")}) // key */@Table(name = "Publishers",       indexes = {@Index(name = "Publishers_Names", columnList = "PublisherName")) //可以不設定,預設為AccessType.PROPERTY,表示表的列名字是根據getter和setter方法,即Java Bean property;另一個值是就AccessType.FIELD,即根據field的名字。一般應使用預設值。@Access(AccessType.FIELD)public class Publisher implements Serializable{ .....}
主鍵映射:單一主鍵
@Entity(name = "PublisherEntity")@Table(name = "Publishers", indexes = {@Index(name = "Publishers_Names", columnList = "PublisherName"))})public class Publisher implements Serializable{    private long id;  //這就是field    private String name;    private String address;    /* 對於標記annotation,我們要麼都載入field上,要麼都載入property上,不要兩者混用,避免出現混淆     *【1】我們首先要設定surrogate key,也就是SQL中的primary key,可能是單列,也可能是聯合主鍵。*/    // 1.1】設定主鍵。主鍵是唯一的,如果代碼沒有設定@Id,如果有getId和setId,則視為該方法有@Id。    @Id     //【2】設定自動產生的值    // 2.1】如果是MySQL主鍵的AUTO_INCREMENT(Microsoft SQL Server和Sysbase中稱為IDENTITY),為@GeneratedValue(strategy = GenerationType.IDENTITY)    /* 2.2】通過產生器的方式來設定主鍵的產生。產生器是全域有效,在一處設定後,其他也可以使用,產生器的定義可以在class上,但是@GeneratedValue必須在property或者field上。有GenerationType.SEQUENCE和GenerationType.TABLE兩種,對應@SequenceGenerator和@TableGenerator。    ➤ @SequenceGenerator,如何產生主鍵的值(在Oracle中稱為sequence,也就是流水號的生產方式),屬性有initialValue(初始值預設0),allocationSize(步進值,預設50)。    ➤ @TableGenerator,使用一個專門的表格來存放當前的流水號,如例子所示:使用了表SurrogateKeys,主鍵為TableName,值為Publishers,另一列為KeyValue,初始值11923,步進為1;也就是說在當前表增加一個entry時,表SurrogateKeys中TableName=Publishers的KeyValue加一,並將這個值作為本entity的id(列為PublisherId)的值。需要注意的是table,pkColumnName,pkColumnValue,valueColumnName的預設值並沒有在JPA中規定,因此不同的實現會有不同,為了避免歧義,建議明確定義。然而,我們很少使用這種方式,可能用於曆史遺留項目。GenerationType.IDENTITY或GenerationType.SEQUENCE能滿足我們的要求*/    @GeneratedValue(strategy = GenerationType.TABLE, generator = "PublisherGenerator")    @TableGenerator(name = "PublisherGenerator", table = "SurrogateKeys",                    pkColumnName = "TableName", pkColumnValue = "Publishers",                    valueColumnName = "KeyValue", initialValue = 11923, allocationSize = 1)    //【3】@Column用於對列的設定    // @Column的參數 name:指定列名;insertable和updatable是許可權    // @Column的schema相關參數    /*   -  nullable:NULL或者NOT NULL;     *   -  unique相當於@UniqueConstraint,預設為false;     *   -  length用於VARBINARY和VARCHAR的長度,預設為255;     *   -  scale和precision用於Decimal;     *   -  columnDefinition指定產生列的SQL,但是由於不同SQL Server在語句上會有區別,一旦使用,就很難遷移到其他資料庫,因此不建議 */    @Column(name = "PublisherId")    public final long getId() {        return id;    }}
主鍵映射:複合主鍵

方式1:使用@IdClass

表格SomeJoinTable有聯合組件(fooParentTableSk,barParentTableSk),代碼如下:

public class JoinTableCompositeId implements Serializable{    private long fooParentTableSk;    private long barParentTableSk;    public long getFooParentTableSk() { ... }    public void setFooParentTableSk(long fooParentTableSk) { ... }    public long getBarParentTableSk() { ... }    public void setBarParentTableSk(long barParentTableSk) { ... }}@Entity@Table(name = "SomeJoinTable")@IdClass(JoinTableCompositeId.class)public class JoinTableEntity implements Serializable{    private long fooParentTableSk;    private long barParentTableSk;    ...    @Id    public long getFooParentTableSk() { ... }    public void setFooParentTableSk(long fooParentTableSk) { ... }    @Id    public long getBarParentTableSk() { ... }    public void setBarParentTableSk(long barParentTableSk) { ... }    ...}

方式2:使用@EmbeddedId

我們看到方式1中,在getter和setter的代碼有重複,可以採用下面的方式:

@Embeddablepublic class JoinTableCompositeId implements Serializable{    private long fooParentTableSk;    private long barParentTableSk;    public long getFooParentTableSk() { ... }    public void setFooParentTableSk(long fooParentTableSk) { ... }    public long getBarParentTableSk() { ... }    public void setBarParentTableSk(long barParentTableSk) { ... }}@Entity@Table(name = "SomeJoinTable")public class JoinTableEntity implements Serializable{    private JoinTableCompositeId id;    @EmbeddedId    public JoinTableCompositeId getId() { ... }    public void setId(JoinTableCompositeId id) { ... }}
資料類型映射
JPA中property的資料類型 資料庫中column的資料類型
short,Short SMALLINT, INTEGER, BIGINT或相應的類型
int,Integer INTEGER, BIGINT或相應的類型
long,Long BIGINT或相應的類型
float, Float, double, Double, BigDecimal DECIMAL 或相應的類型
byte,Byte BINARY, SMALLINT, INTEGER, BIGINT或相應的類型
char,Char CHAR, VARCHAR, BINARY, SMALLINT, INTEGER, BIGINT或相應的類型
boolean,Boolean BOOLEAN, BIT, SMALLINT, INTEGER, BIGINT, CHAR, VARCHAR或相應的類型
byte[],Byte[] BINARY, VARBINARY或相應的類型
char[], Character[],String CHAR, VARCHAR, BINARY,VARBINARY或相應的類型
java.util.Date, Calendar DATE, DATETIME, TIME或相應的類型,需要加上@Temporal
java.sql.Timestamp DATETIME
java.sql.Date DATE
java.sql.Time Time
Enum SMALLINT, INTEGER, BIGINT, CHAR, VARCHAR或相應的類型,可以通過@Enumerated來變更儲存方式,在後面介紹。
Serializable VARBINARY或相應的類型,用於Java的序列化和還原序列化

/*使用@Basic可以進行上表中的類型匹配,optional表示是否可以為null,本例表示NOT NULL。對於原型,如int,double等是沒有null這一說的,因此在原型時,不設定option說明,資料庫中必定是NOT NULL */@Basic(optional=false)public final String getIsbn() {   return isbn;}

設定Persistence Persistent Unit Scope是配置和Entity類,其中的EntityManager執行個體只能方位該持續化單元,不能越界。Entity類可以屬於多個持續化單元。持續化單元通過persistence.xml配置,必須放置在META-INF/下。一個web項目有多個META-INF:

mappings.war!/META-INF  這不在classes/目錄下,不能被class檔案訪問,用來放置Servlet容器所需的檔案,所以不能放這裡mappings.war!/WEB-INF/classes/META-INF  放在這裡,在eclipse中,即在resources/下串接META-INF/mappings.war!/WEB-INF/lib/something.jar!/META-INF  這是其他jar包所帶的META-INF目錄,我們也放不進去,而且其作用只在該jar包內


下面是例子的persistence.xml:

<?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"            xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence                                http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"            version="2.1">    <!-- 有一個或者多個persistence-unit -->    <!-- transaction-type:在J2EE應用伺服器中預設為JTA(Java Transaction API),在J2SE和簡單的servlet容器中預設為標準本地事務 RESOURCE_LOCAL。為避免歧義,我們應明確設定 -->    <!-- persistence-unit裡面可以為空白,但是如果設定,必須要順序 -->    <persistence-unit name="EntityMappingsTest" transaction-type="RESOURCE_LOCAL">        <!-- 首先是<description>,小例子不提供 -->        <!-- provider:具體的javax.persistence.spi.PersistenceProvider實現,預設值為classpath中第一個JPA的實現 -->        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>        <!-- 如果persistence-unit的transaction-type為JTA,使用<jta-data-source>,如果為RESOURCE_LOCAL,使用<non-jta-data-source>。他們的區別在於後者使用EntityTransaction介面,而前者使用UserTransaction介面,預設是前者(JTA)。它們均採用JNDI(Java Naming and Directory Interface)方式,給出資料來源。 -->        <non-jta-data-source>java:comp/env/jdbc/learnTest</non-jta-data-source>        <!-- <mapping-file>:基於classpath的XML mapping file,如果不指定,預設為orm.xml,可以設定多個mapping-file -->        <!-- <jar-file>:JPA實現將對jar包進行綁定標記掃描,如果裡面有@Entity,@Embeddable,@javax.persistence.MappedSuperclass或者@javax.persistence.Converter,加入本持續化單元中,可以設定多個jar-file -->        <!-- <class>:JPA實現將對這個class加入到持續化單元中,這個class必須帶有@Entity,@Embeddable,@javax.persistence.MappedSuperclass或者@javax.persistence.Converter。可以設定多個class -->        <!-- 設定<exclude-unlisted-classes/>或者<exclude-unlisted-classes>true</exclude-unlisted-classes>表示只關注在jar-file和在class中所設定的,不掃描其他。刪除<exclude-unlisted-classes/>或者<exclude-unlisted-classes>false</exclude-unlisted-classes>則表示將掃描classpath位置;如果本檔案在JAR檔案,則掃描JAR檔案的classes,如果本檔案位於classes中的某個特定目錄,則只掃描該目錄下的檔案(例如指定到某個package)。 -->        <exclude-unlisted-classes>false</exclude-unlisted-classes>        <!-- <shared-cache-mode>:是否緩衝entity,-->        <!-- ➤ NONE表示不緩衝,-->        <!-- ➤ ALL表示緩衝所有的entities。-->        <!-- ➤ ENABLE_SELECTIVE 表示只緩衝帶有@Cacheable或者@Cacheable(true)標識的entity -->        <!-- ➤ DISABLE_SELECTIVE 表示除了@Cacheable(false)外均緩衝 -->        <!-- ➤ UNSPECIFIED 表示有JPA的提供者來決定,Hibernate ORM預設為ENABLE_SELECTIVE,但採用這種方式,對於移植可能會存在混淆,應明確設定 -->        <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>        <!-- <validation-mode> -->        <!-- ➤ NONE表示不使用Bean validator,-->        <!-- ➤ CALLBACK表示在寫操作(insert,update,delete)前進行validate -->        <!-- ➤ AUTO,如果classpath中存在Bean validator provider,則CALLBACK,不存在則NONE -->        <!-- 如果使用validate,而我們自訂了spring framework的validator,JPA將忽略這個自訂。因此建議使用NONE,在資料持久化之前進行校正,而不是在持久化這個層面。 -->        <validation-mode>NONE</validation-mode>        <!-- <properties>以name-value的方式提供其他JPA屬性(如JDBC串連,使用者,密碼,schema產產生設定等)以及提供者特有的屬性(如Hibernate設定)。 -->        <properties>            <!-- 禁止schema產生,即不根據entity建立表格 -->            <property name="javax.persistence.schema-generation.database.action" value="none" />        </properties>    </persistence-unit></persistence>

持續化的代碼 我們以servlet為例,示範如何查詢表格和insert表格。

@WebServlet(    name ="EntityServlet",    urlPatterns = "/entities",    loadOnStartup = 1)public class EntityServlet extends HttpServlet {    private static final long serialVersionUID = 1L;    private final Random random;    //Entity管理器:在初始化時擷取,在結束是關閉    private EntityManagerFactory factory;           public EntityServlet() {        super();        try {            this.random = SecureRandom.getInstanceStrong();        } catch (NoSuchAlgorithmException e) {            throw new IllegalStateException(e);        }    }    /** 【1.1】在初始化時建立EntityManagerFactory。持續化單元的名字見persistence.xml。      * 對於完全J2EE server(Tomcat不是),不需要這樣,採用:      *   @PersistenceContext("EntityMappingsTest")      *   EntityManagerFactory factory; */    @Override    public void init() throws ServletException {        super.init();        this.factory = Persistence.createEntityManagerFactory("EntityMappingsTest");    }    /**【1.2】在結束時關閉EntityManagerFactory */    @Override    public void destroy() {        this.factory.close();        super.destroy();    }    /** 【2】我們在doGet中示範擷取表格內容,其中,示範了對transaction處理的常規代碼 */    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        EntityManager manager = null;        EntityTransaction transaction = null;        try{            manager = this.factory.createEntityManager();            transaction = manager.getTransaction();            transaction.begin();            //讀表Publisher(簡單的採用全擷取的方式)                         CriteriaBuilder builder = manager.getCriteriaBuilder();//返回用於CriteriaQuery objects的CriteriaBuilder執行個體            CriteriaQuery<Publisher> q1 = builder.createQuery(Publisher.class);            Root<Publisher> q1Root = q1.from(Publisher.class);            TypedQuery<Publisher> queryResult = manager.createQuery(q1.select(q1Root));            request.setAttribute("publishers", queryResult.getResultList());            //讀表Author(簡單的採用全擷取的方式)               CriteriaQuery<Author> q2 = builder.createQuery(Author.class);            request.setAttribute("authors", manager.createQuery(q2.select(q2.from(Author.class))).getResultList());            CriteriaQuery<Book> q3 = builder.createQuery(Book.class);            request.setAttribute("books", manager.createQuery(q3.select(q3.from(Book.class))).getResultList());            transaction.commit();            request.getRequestDispatcher("/WEB-INF/jsp/view/entities.jsp").forward(request, response);        }catch(Exception e){            if(transaction != null && transaction.isActive())                transaction.rollback();            e.printStackTrace(response.getWriter()); //簡單地在頁面輸出,正式項目不會如此處理        }finally{            if(manager != null && manager.isOpen())                manager.close();        }    }    /** 【3】我們在doPost中示範insert */    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        EntityManager manager = null;        EntityTransaction transaction = null;        try{            manager = this.factory.createEntityManager();            transaction = manager.getTransaction();            transaction.begin();            Publisher publisher = new Publisher();            publisher.setName("John Wiley & Sons");            publisher.setAddress("1234 Baker Street");            manager.persist(publisher);            Author author = new Author();            author.setName("Nicholas S. Williams");            author.setEmailAddress("nick@example.com");            manager.persist(author);            Book book = new Book();            book.setIsbn("" + this.random.nextInt(Integer.MAX_VALUE));            book.setTitle("Professional Java for Web Applications");            book.setAuthor("Nicholas S. Williams");            book.setPublisher("John Wiley & Sons");            book.setPrice(59.99D);            manager.persist(book);            transaction.commit();            response.sendRedirect(request.getContextPath() + "/entities");        }catch(Exception e){            if(transaction != null && transaction.isActive())                transaction.rollback();            e.printStackTrace(response.getWriter());        }finally{            if(manager != null && manager.isOpen())                manager.close();        }    }}

相關連結: 我的Professional Java for Web Applications相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.