zoukankan      html  css  js  c++  java
  • JPA Introduction

    1. Summary

    1.1 what is JPA

    JPA is short for Java Persistence API, which is one of the Java EE 5 specification. it standardize the ORM persistence technology for Java developers. It is not a product but an interface.


    1.2 why use JPA

    - JPA is standardized specification and part of EJB3 specification

    - Many free ORM frameworks are available with can be used to develop applications of any size

    - Application developed in JPA is portable across many servers and persistence products (ORM frameworks).

    - Can be used with both Java EE and Java SE applications

    - Java SE 5 features such as annotations can be used

    - Both annotations and xml based configuration support


    1.3 Provider

    JPA only provides interface which needs an ORM implementation to work and persist Java objects.

    ORM frameworks that can be used with JPA(implement JPA interface) are: Hibernate, Toplink, MyBatis, Open JPA etc.

    We choose Hibernate as the ORM framework.


    1.4 JDK Environment

    JPA can be used with both Java EE and Java SE application.

    We use it in Java SE 6 environment.



    1. Use Hibernate JPA With Plain Java SE

    With a standalone JPA provider (here we use Hibernate), JPA can be used in Java SE environment.

    To use JPA compliant persistence technology, we have to config classpath, mapping meta data, persistence unit.

     

    2.1 Config Classpath

    Before start a project, we first need to set up the project, download the necessary library and build the classpath, for Hibernate with JPA, the necessary core libraries are:

    hibernate-jpa-2.0-api-1.0.0.Final.jar – the JPA interface

    hibernate3.jar – Hibernate library(now latest stable version is 3.6.7)


    After got these libraries, add them to the project's classpath.

     

    2.2 Create Entity Class

    An entity is a lightweight persistent domain object.

     

    It is just like standardized Java bean. Some requirements need to follow:

    The entity class must be annotated with the @Entity annotation or denoted in the XML descriptor as an entity.(as described in Section 2.3)

    The entity class must have a no-arg constructor. The entity class may have other constructors as well.

    The no-arg constructor must be public or protected.

    The entity class must be a top-level class. An enum or interface should not be designated as an entity.

    The entity class must not be final. No methods or persistent instance variables of the entity class may be final.

    If an entity instance is to be passed by value as a detached object (e.g., through a remote interface), the entity class must implement the Serializable interface.

    Entities support inheritance, polymorphic associations, and polymorphic queries.

    Both abstract and concrete classes can be entities.

    The persistent state of an entity is represented by instance variables, which may correspond to Java Beans properties.

     

    e.g.

    @Entity

    @Table(name="cargo_info")

    publicclass CargoAspect implements Serializable{


    privatestaticfinallongserialVersionUID = 1L;

    @Id

    @GeneratedValue(strategy = GenerationType.AUTO)

    @Column(name="id",nullable=true,unique=true)

    private Integer id;

    @Column(name="cargo_booking_no",updatable=false,insertable=false)

    private String bookingNo;

    @GeneratedValue(strategy = GenerationType.SEQUENCE)

    @Column(name="sequence")

    private String sequence;

    @Column(name="commodity")

    private String commodity;

    @ManyToOne(optional=false)

    @JoinColumn(name="cargo_booking_no",referencedColumnName="booking_no")

    private BookingBasic bookingBasic;

     

    public CargoAspect(){}

     

    public String getBookingNo() {

    returnbookingNo;

    }

    publicvoid setBookingNo(String bookingNo) {

    this.bookingNo = bookingNo;

    }

    public String getSequence() {

    returnsequence;

    }

    publicvoid setSequence(String sequence) {

    this.sequence = sequence;

    }

    public String getCommodity() {

    returncommodity;

    }

    publicvoid setCommodity(String commodity) {

    this.commodity = commodity;

    }

    public BookingBasic getBookingBasic() {

    returnbookingBasic;

    }


    publicvoid setBookingBasic(BookingBasic bookingBasic) {

    this.bookingBasic = bookingBasic;

    }


    }


    2.3 Config Mapping Metadata

    Mapping metadata is used to define the mapping between Java objects and a relational database. JPA supports both standardized Java language metadata annotations and XML descriptors.


    Compared to XML metadata, annotations have some superiority:

    they are much more convenient and reduce the lines of metadata significantly;

    they are type-safe, they support autocompletion in your IDE as you type;

    they make refactorying of classes and properties easier.

    Developers can override or replace all annotated metadata with XML metadata files.


    We choose to use annotations metadata.


    Developers who may worry about changing the source code whenever there is a need to update the mapping, can choose to use additional XML metadata to override or add mapping info as described in section 2.3.2.

     

    2.3.1 Annotations Metadata

    JPA defines annotations types in the package javax.persistence.

    The commonly used annotations are:

    @Entity: it is applied to a class, specifies that the class is an entity. Annotation element name can be specified to refer to the entity in queries, default value is the unqualified name of the entity class.


    @Table: it is applied to an entity class, specifies the primary table for the annotated entity. 4 available optional annotation elements can be specified: name, catalog, schema, uniqueConstraints.

    e.g.

    @Entity

    @Table(name="booking")

    publicclass BookingBasic implements Serializable{


    @Column: it is applied to an property of the annotated entity, it is used to specify a mapped column for a persistent property. 7 available optional elements can be specified for this annotation: name, unique, nullable, insertable, updatable, columnDefinition, table, length, precision, scale. If @Column annotation is not specified, it is assumed that the column name in the table is same as the property name.

    e.g.

    @Column(name="customer")

    private String customer


    @Id: it is applied to an property of the annotated entity, specifies the primary key properties of an entity. By default, the mapped column is assumed to be the primary column of the primary table. No element need to be specified.


    @GeneratedValue: it is applied to the primary key property of the annotated entity, it provides for the specification of generation strategies for the values of primarykeys. 2 available optional elements can be specified: strategy, generator. If not specified, they will use the default value.

    e.g.

    @Id

    @GeneratedValue(strategy = GenerationType.AUTO)

    @Column(name="id",nullable=false,unique=true)

    privatelongid;


    @JoinColumn: it is applied to an entity type property in the annotated entity, it is used to specify a mapped column for joining an entity association. The available optional emelement are: name, referencedColumn, unique, nullable, insertable, updatable, columnDefinition, table.


    @OneToOne: it is usually applied to an entity type property in the annotated entity, it defines a single-valued association to another entity that has one-to-one multiplicity. By default, the mapped table of the two annotated entity are assumed to be one-to-one relationship. The available optional elements are: targetEntity, cascade, fetch, optional, mappedBy.


    @OneToMany: same as @OneToOne, it is used to define the relationship of two annotated entity. It defines a many-valued association with one-to-many multiplicity.

    the elements can be specified are: targetEntity, cascade, fetch, mappedBy.

    e.g.

    In BookingBasic class:@OneToMany(mappedBy="bookingBasic",targetEntity=ServiceCharge.class,

    fetch=FetchType.LAZY,cascade=CascadeType.ALL)

    private List<ServiceCharge> serviceCharges = Collections.emptyList();

     

    In ServiceCharge class:

    @ManyToOne(optional=false)

    @JoinColumn(name="service_booking_no",

    referencedColumnName="booking_no")

    private BookingBasic bookingBasic;


    For more detail explanation of the annotation elements and other standardized annotations, ple refer to Chapter 9 of the specification document - http://192.168.18.224:8888/svn/Work_Space/Research/scarlett/booking-service/doc/ejb-3_0-fr-spec-persistence.pdf


    2.3.2 XML Metadata

    JPA also supports XML metadata, it is intended to serve as both an alternative and an overrideing mechanism for annotation metadata.

    We have not used the XML metadata in application, we may need to consider it as a mixed for more involved app. For more detail info about it Developers can refer to the Chapter 10 of the specification document - http://192.168.18.224:8888/svn/Work_Space/Research/scarlett/booking-service/doc/ejb-3_0-fr-spec-persistence.pdf

    It defines what Elements/Attributes can be used in the XML metadata file.


    2.4 Config Persistence Unit

    In JPA domain, there is a concept – PersistenceContext, A PersistenceContext is a set of

    entity instances, for each entity instance, there is a unique entity identity.

     

    A PersistenceContext is essentially a cache. It also tends to have its own non-shared database connection. Within the PersistenceContext, the entity instances and their lifecycle are managed.


    JPA defines the interface EntityManager(EM for short) to interact with the PersistenceContext, EM provides API to create, remove, find entities by their primary keys. An EM actually represents a PersistenceContext.


    You must use the EntityManagerFactory(EMF for short) to create an EM, EMF is another interface provided by JPA, It can create an EM(the PersistenceContext/cache) according to the configuration file persistence.xml.


    The persistence.xml file is located in the META-INF directory of the root of the persistence-unit on the classpath.


    A persistence.xml file consists of one or more persistence-units, with a persistence-unit, you create an EMF, and thus a PersistenceContext will also be created, A PersistenceContext is an instance created based on the configurations defined in a persistence-unit, An EM provided by the EMF created based on a persistence-unit manages the PersistenceContext instance.


    Each persistence-unit represents a particular datasource, mapping metadata and together with other configurations.


    In the persistence-unit, the attributes and elements we can define are:

    name: it must be specified for the persistence-unit element, it defines the name for the persistence-unit, this name is used to identify a persistence-unit when create EMF.


    transaction-type: this attribute is optional. When not set, the default value is RESOURCE_LOCAL in Java SE and JTA in Java EE. The available value are RESOURCE_LOCAL and JTA. It is used to specify whether the EM created by the EMF for the persistence-unit must be JTA EMs or RESOURCE_LOCAL EMs. If it is JTA EMs, the EM will be bound to the current JTA transaction during runtime, it is RESOURCE_LOCAL EMs, the EM have to create local EntityTransaction in the application.

    For detail info about transaction config and usage, can refer to the document JTA Lesson - http://192.168.18.224:8888/svn/Work_Space/Research/scarlett/booking-service/doc/JTA_lesson_v1.1.doc


    provider: specify the name of the persistence provider's javax.persistence.spi.PersistenceProvider class. The provider must be specified if the application is dependent upon a particular persistence provider being used.

    e.g.

    <provider>org.hibernate.ejb.HibernatePersistence</provider>


    class: explicitly list a managed persistence class.

    In Java SE environment, generally all classes that are to be managed must be enumerated in each persistence-unit. In Hibernate, it provides an auto detection which can auto detect the entity class which are annotated with annotation metadata or listed in the hibernate *.hbm.xml maping file. If not set, the default value is 'class,hbm'.


    properties: it is to specify vendor-specific properties that apply to the persistence unit and its EMF configuration. So if Hibernate is the persistence provider, the property element configuration under properties should according to Hibernate's configuration.

    For what properties can be configured for Hibernate, can refer to Chapter 3 Configuration of hibernate reference document - http://192.168.18.224:8888/svn/Work_Space/Research/scarlett/booking-service/doc/hibernate_reference.pdf


    e.g.

    <persistence-unit name="booking">

    <provider>org.hibernate.ejb.HibernatePersistence</provider>

     

    <properties>

    <property name="hibernate.archive.autodetection" value="class"/>

    <property name="hibernate.show_sql" value="true"/>

    <property name="hibernate.format_sql" value="true"/>

    <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>

    <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/Bookings"/>

    <property name="hibernate.connection.username" value="root"/>

    <property name="hibernate.connection.password" value="ACahlof"/>

    <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>

    <!-- after first run the application, should comment it, else it will drop and create table each time

    <property name="hibernate.hbm2ddl.auto" value="create"/> -->

    </properties>

    </persistence-unit>


      1. Use JPA In Application

    After setting up the project with importing necessary library(as described in 2.1), creating Entity with mapping metadata(as described in 2.2, 2.3), configuring EMF(as described in 2.4), we can use the EMF/EM to create, remove, find entities.


    EM provides several API to manage the PersistenceContext such as find(), flush(), persist(), refresh() etc. For complete and detail API, pls refer to the Chapter 3.1 of JPA specification document - http://192.168.18.224:8888/svn/Work_Space/Research/scarlett/booking-service/doc/ejb-3_0-fr-spec-persistence.pdf

    e.g.

    EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistence-unit);

    EntityManager em=emf.createEntityManager();

    em.persist(obj);



    1. Use Hibernate JPA With Spring

    Spring provides good way to injection, we can use Spring to inject EMF to application Java bean with the help of its IOC container by configuring in the XML configuration file.


    The JPA specification has defined two kinds of EM: Application-managed and Container- managed. For detail features of application-managed and container-managed EM, can refer to the chapter 5 EntityManagers and Persistence Context of JPA specification - http://192.168.18.224:8888/svn/Work_Space/Research/scarlett/booking-service/doc/ejb-3_0-fr-spec-persistence.pdf


    Application-managed EMs are created by an EMF obtained by calling the createEntityManagerFactory() method of the PersistenceProvider. Meanwhile, container- managed EMs are created by EMF which will be created by the container. With the application- managed approach, application will responsible for the creation and destruction of the EMF/EM, while with the container-managed approach, container will take charge of the creation and destruction.

     

    When come to the Spring, Spring hides the intricate details of dealing with two kinds of EMF, the only difference for developers is how to configure the EMF with Spring.


    3.1 Application-managed EMF

    Application-managed EMF derive most of their configuration information from the configuration file – persistence.xml. Thus application code is responsible for the creation of an EMF through Persistence's createEntityManagerFactory() method. And developers only need to configure little in the Spring configuration file, we should use the class LocalEntityManagerFactoryBean:

    e.g.

    <bean id="emf"

    class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">

    <property name="persistenceUnitName" value="booking" />

    </bean>


    Spring can find the persistence-unit by the persistenceUnitName if only the persistence.xml is put under META-INF on the classpath.


    With Spring's support, we do not need to deal with the creation and destruction of the EMF, Spring can take over it. Compared with LocalEntityManagerFactoryBean, LocalContainerEntityManagerFactoryBean is more flexible, you can override the location of the persistence.xml file, specify the JDBC DataSources to link to, etc. Furthermore, it allows for pluggable class instrumentation through Spring's loadTimeWeaver abstraction, instead of being tied to a special VM agent specified on JVM startup. For LoadTimeWeaver, can refer to the chapter 4.13 Registering a LoadTimeWeaver of Spring reference document - http://192.168.18.224:8888/svn/Work_Space/Research/scarlett/booking-service/doc/spring-framework-reference.pdf


    So we recommend developers to use the LocalContainerEntityManagerFactoryBean when working with Spring.


    3.2 Container-managed EMF

    3.2.1 Configure EMF

    When use container-managed approach, instead of configuring data source details in persistence.xml, developer should configure the information in the Spring application context XML file. And we should use the class LocalContainerEntityManagerFactoryBean:

    e.g.

    <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    <property name="dataSource" ref="dataSource" />

    <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />

    <property name="persistenceUnitName" value="org.drools.persistence.jpa"/>

    <property name="jpaPropertyMap">

    <map>

    <entry key="hibernate.transaction.flush_before_completion" value="true"/>

    <entry key="hibernate.transaction.auto_close_session" value="true"/>

    <entry key="hibernate.current_session_context_class" value="jta"/>

    <entry key="hibernate.connection.release_mode" value="auto"/>

    </map>

    </property>

    </bean>

     

    Here we have configured the dataSource, jpaVendorAdapter and persistenceUnitName property. The dataSource and jpaVendorAdapter property refer to a Spring-configured data source and JPA vendor.

     

    3.2.2 Configure Data Source

    For data source, Any implementation of javax.sql.DataSource is appropriate. Although a data source may be configured in persistence.xml file, the data source specified through this property takes precedence. Besides we can also use the data source when configure transaction manager in Spring later. So when with Spring, we should always configure the data source in Spring configuraton file.

    e.g.

    <bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close">

    <property name="className" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />

    <property name="driverProperties" >

    <props>

    <prop key="URL">jdbc:mysql://localhost:3306/DroolsTasks</prop>

    <prop key="user">root</prop>

    <prop key="password">ACahlof</prop>

    </props>

    </property>

    <property name="uniqueName" value="jdbc/task" />

    <property name="minPoolSize" value="0" />

    <property name="maxPoolSize" value="5" />

    </bean>

     

    Above data source configuration is applicable for JTA transaction, the data source bitronix.tm.resource.jdbc.PoolingDataSource has implemented the avax.sql.DataSource and wrapped the MySQL's XADataSource implementation- com.mysql.jdbc.jdbc2.optional.MysqlXADataSource. An XADataSource is a data source that can participate in an distributed transaction, When work with distributed transaction, we should use XADataSource. For more detail about the detail transaction, can refer to JTA lesson document - http://192.168.18.224:8888/svn/Work_Space/Research/scarlett/booking-service/doc/JTA_lesson_v1.1.doc

     

    3.2.3 Configure JPA Vendor

    For JPA vendor, it is to specifies the particular JPA Implementation to use. Spring comes with a handful of JPA vendor adaptors to choose from:

    EclipseLinkJpaVendorAdapter

    HibernateJpaVendorAdapter

    OpenJpaVendorAdapter

    TopLinkJpaVendorAdapter


    Here we choose the Hibernate as a JPA implementation, so we will configure it in Spring application context with a HibernateJpaVendorAdapter:

    e.g.

    <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">

    <property name="database" value="MYSQL" />

    <property name="showSql" value="true"/>

    <property name="generateDdl" value="false"/>

    <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />

    </bean>

     

    Several properties are set on the vendor adaptor, the most important one is the database property, where we have specified the MYSQL as the database we will be using. Other supported database are: DB2, DERBY, H2, HSQL, INFORMIX, ORACLE, POSTGRESQL, SQLSERVER, SYBASE.

     

    3.2.4 Configure Persistence Unit

    When with Spring, we should move all the EMF configuration to Spring application context file, and do not need to configure anything in the persistemce.xml file, but we still need a persistence.xml file under META-INF on the classpath, we can config an empty persistence- unit in the persistence.xml file:

    e.g.

    <persistence-unit name="org.drools.persistence.jpa" transaction-type="JTA">


    </persistence-unit>

    As tested, we should correctly set the transaction-type property for the persistence-unit, just as we have done in the section 2.4.

     

    3.2.5 Enable JPA Bases DAO

    After having configured the EMF in Spring, how to enable the JPA based application? There are two ways can be used:

    3.2.5.1 Inject EM into DAO

    Configure in Spring application context file:

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

     

    Configure in DAO:

    e.g.

    @PersistenceContext(unitName = "emf")

    private EntityManager em;

     

    Then in the method of the DAO, we can directly use the em .

    e.g.

    publicvoid saveBooking(BookingBasic booking) {


    em.persist(booking);


    }


     

    Note that in the DAO, the EM is annotated with @PersistentContext, it indicates that an instance of EM should be inject into em, to enable EM injection in Spring, we should set the PersistenceAnnotationBeanPostProcessor in Spring configuration file.

     

    3.2.5.2 Inject EMF into DAO

    Configure in Spring context file:

    e.g.

    <!-- inject the EMF to FormSaveHandler -->

    <bean id="formSaveHandler" class="com.icil.booking.handler.FormSaveHandler">

    <property name="emf" ref="emf" />

    </bean>

     

    Configure in DAO:

    e.g.

    private EntityManagerFactory emf;

    publicvoid setEmf(EntityManagerFactory entityManagerFactory){

    this.emf = entityManagerFactory;

    }

     

    Then in the method of the DAO, we can directly use the emf.

    e.g.

    publicvoid saveBooking(BookingBasic booking) {


    EntityManager em=emf.createEntityManager();

    em.persist(booking);


    }

     

    In above setting, we set the EMF as the DAO's property with the set method, in the Spring configuration file, we pass the Spring configured emf to the EMF property. Then we can directly use the EMF in the DAO.


    For configuration schema in Spring can refer to the Appendix C of Spring reference document - http://192.168.18.224:8888/svn/Work_Space/Research/scarlett/booking-service/doc/spring-framework-reference.pdf


    For simplicity, we can choose to inject the EM just like 3.2.5.1 does, but when doing in this way, the property em cannot be specified as static property, then static method cannot use this em, then we can choose the way in 3.2.5.2, the emf can be specified as static property and used in static method.


    So now we have described how to use JPA with Spring, and our current application just uses this framework, we have show some snippet of our current application configuration in above paragraphs, for the complete JPA configuration of current application, can refer to below:

    <beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xmlns:tx="http://www.springframework.org/schema/tx"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd

    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

     

    <bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init- method="init" destroy-method="close">

    <property name="className" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />

    <property name="driverProperties" >

    <props>

    <prop key="URL">jdbc:mysql://localhost:3306/DroolsTasks</prop>

    <prop key="user">root</prop>

    <prop key="password">ACahlof</prop>

    </props>

    </property>

    <property name="uniqueName" value="jdbc/task" />

    <property name="minPoolSize" value="0" />

    <property name="maxPoolSize" value="5" />

    </bean>


    <bean id="dataSource1" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close">

    <property name="className" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />

    <property name="driverProperties" >

    <props>

    <prop key="URL">jdbc:mysql://localhost:3306/Bookings</prop>

    <prop key="user">root</prop>

    <prop key="password">ACahlof</prop>

    </props>

    </property>

    <property name="uniqueName" value="jdbc/booking" />

    <property name="minPoolSize" value="0" />

    <property name="maxPoolSize" value="3" />

    </bean>

     

    <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    <property name="dataSource" ref="dataSource" />

    <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />

    <property name="persistenceUnitName" value="org.drools.persistence.jpa" />

    <property name="jpaPropertyMap">

    <map>

    <entry key="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.BTMTransactionManagerLookup"/>

    <entry key="hibernate.transaction.flush_before_completion"

    value="true"/>

    <entry key="hibernate.transaction.auto_close_session"

    value="true"/>

    <entry key="hibernate.current_session_context_class"

    value="jta"/>

    <entry key="hibernate.connection.release_mode"

    value="auto"/>

    </map>

    </property>

    </bean>

     

    <!-- this is to test different EMF use same dataSource

    <bean id="emf3" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    <property name="dataSource" ref="dataSource" />

    <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />

    <property name="persistenceUnitName" value="org.drools.persistence.jpa"/>

    <property name="jpaPropertyMap">

    <map>

    <entry key="hibernate.transaction.manager_lookup_class"

    value="org.hibernate.transaction.BTMTransactionManagerLookup"/>

    <entry key="hibernate.transaction.flush_before_completion"

    value="true"/>

    <entry key="hibernate.transaction.auto_close_session"

    value="true"/>

    <entry key="hibernate.current_session_context_class"

    value="jta"/>

    <entry key="hibernate.connection.release_mode"

    value="auto"/>

    </map>

    </property>

    </bean>

    -->

     

    <bean id="emf1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    <property name="dataSource" ref="dataSource1" />

    <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />

    <property name="persistenceUnitName" value="booking-jta" />

    <property name="jpaPropertyMap">

    <map>

    <entry key="hibernate.transaction.manager_lookup_class"

    value="org.hibernate.transaction.BTMTransactionManagerLookup"/>

    <entry key="hibernate.transaction.flush_before_completion"

    value="true"/>

    <entry key="hibernate.transaction.auto_close_session"

    value="true"/>

    <entry key="hibernate.current_session_context_class"

    value="jta"/>

    <entry key="hibernate.connection.release_mode"

    value="auto"/>

    </map>

    </property>

    </bean>


    <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">

    <property name="database" value="MYSQL" />

    <property name="showSql" value="true"/>

    <property name="generateDdl" value="false"/>

    <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />

    </bean>


    <!-- inject the EMF to FormSaveHandler -->

    <bean id="formSaveHandler" class="com.icil.booking.handler.FormSaveHandler">

    <property name="emf" ref="emf1" />

    </bean>

     

    <!-- inject the EMF to BookingProcess -->

    <bean id="bookingProcess" class="com.icil.booking.as.drools.process.BookingProcess">

    <property name="emf" ref="emf" />

    </bean>

     

    <!-- inject the EMF to TaskHandler -->

    <bean id="taskHandler" class="com.icil.drools.task.handler.TaskHandler">

    <property name="emf" ref="emf" />

    <!--this is to test different EMF use same dataSource

    <property name="emf3" ref="emf3" /> -->

    </bean>

    </beans>


    Here we have used two data source, as in our application, we have to access two databases, one is DroolsTasks, another is Bookings, the dataSource configuration is for DroolsTasks DB, and the dataSource1 configuration is for Bookings. With two data source, we configure two EMF refer to them, one is emf, another is emf1, for DAO bookingProcess and taskHandler which will access DroolsTasks will use the emf, for DAO formSaveHandler with will access Bookings will use the emf1.

     


    1. JPA And Connection

    when come to connection, we have to have some simple knowledge of the connection and connection pool first.


    When the DAO wants to create, find, update data in the database, it first needs to create a connection to database, the connection usually contains the info including server url, port, database name, user name and password, and together with the creation of the connection, a session will be created too between application and database.


    Without the connection pool, when client disconnect, the connection and session will be terminated on both client and server. When with connection pooling enabled, the disconnect method simply changes the status of the connection in the connection pool from 'used' to 'free'. The server is unaware of the change of status, it still recognizes the session as being active. So the connection can be picked up and used again.


    As the creation of connection is an expensive way, with the help of connection pool, statements to the same database can share the same connection, this will save the creation time of connection and improve the efficiency greatly.


    4.1 Configure Connection Pool with Plain Java SE

    When in plain Java SE environment, we can set the connection pool configuration in the persistence.xml file.

    e.g.

    <properties>

    <property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>

    <property name="hibernate.c3p0.acquire_increment" value="4" />

    <property name="hibernate.c3p0.idle_test_period" value="3000" />

    <property name="hibernate.c3p0.max_size" value="100" />

    <property name="hibernate.c3p0.max_statements" value="15" />

    <property name="hibernate.c3p0.min_size" value="5" />

    <property name="hibernate.c3p0.timeout" value="100" />

    </properties>

    The property name connection.provider_class must be specified to indicate which connection provider will be used. Here we specify the c3p0 connection provider.

     

    Hibernate has a built in connection pool, when there is no connection provider specified, the default built in connection pool will be used. Hibernate also supports several other connection pools: c3p0, dbcp, proxool. For configuration of dbcp and proxool, Developers can refer to many material from internet such as http://www.informit.com/articles/article.aspx?p=353736&seqNum=4

     

    Generally, people choose c3p0 connection pool for local transaction situation. And it has good evaluation.


      1. Configure Connection Pool with Spring

    When we come to scenario that using JPA with Spring, the configuration of connection pool is in a different way.

    e.g.

    <beanid="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"destroy- method="close">

    <property name="jdbcUrl" value="${jpa.url}" />

    <property name="user" value="${jpa.username}" />

    <property name="password" value="${jpa.password}" />

    <property name="initialPoolSize" value="1" />

    <property name="minPoolSize" value="1" />

    <property name="maxPoolSize" value="3" />

    <property name="idleConnectionTestPeriod" value="500" />

    <property name="acquireIncrement" value="1" />

    <property name="maxStatements" value="50" />

    <property name="numHelperThreads" value="1" />

    </bean>

    The class of the data source bean is very important, it must be a pooled data source. Here we use the ComboPooledDataSource of c3p0.

     

    The connection pool configuration is configured in the data source bean now, not the property of Hibernate anymore. Thus the connection pool is provided by the pooled data source provider.


    In our current application, as we use the JTA transaction, and BTM(Bitronix) as the JTA implementation, so we use the pooled data source provided by BTM(Bitronix) - bitronix.tm.resource.jdbc.PoolingDataSource, then our configuration of the data source and connection pool can be:

    e.g.

    <bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close">

    <property name="className" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />

    <property name="driverProperties" >

    <props>

    <prop key="URL">jdbc:mysql://localhost:3306/DroolsTasks</prop>

    <prop key="user">root</prop>

    <prop key="password">ACahlof</prop>

    </props>

    </property>

    <property name="uniqueName" value="jdbc/task" />

    <property name="minPoolSize" value="0" />

    <property name="maxPoolSize" value="5" />

    </bean>

     

    4.3 Connection Release Mode

    Hibernate provides several release modes for client to release the connection to connection pool. It is not related with connection pool These release modes are 'standard', and should be supported by any connection pool provider.


    after_statement: means the connection will be released to connection pool after each statement.


    after_transaction: means the connection will be released to the connection pool after transaction completes.


    on_close: mean the connection will be released to the connection pool when the Hibernate Session is closed, in JPA, it is when the EM closes.


    auto: when in local transaction, it will use after_transaction, when in JTA transaction, it will use after_statement.

     

    When with plain Java SE, we can configure like:

    e.g.

    <properties>

    <property name="hibernate.connection.release_mode" value="auto" />

    </properties>

     

    When with Spring, we can configure like:

    e.g.

    <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    <property name="dataSource" ref="dataSource" />

    <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />

    <property name="persistenceUnitName" value="org.drools.persistence.jpa" />

    <property name="jpaPropertyMap">

    <map>

    <entry key="hibernate.connection.release_mode" value="auto"/>

    </map>

    </property>

    </bean>

    Value is set to 'auto' will let Hibernate auto choose the release mode, it will set to 'after_statement' or 'after_transaction' according to the transaction_type.


    4.4 JPA And Connection

    Connection and pooling connection have much more to do with the security and efficiency. We must care much about it.

     

    We have already done some research of the relationship between EMF/EM and connection under the Spring enabled application, that means our EMF configured like section 3 done. In this scenario, we use bitronix.tm.resource.jdbc.PoolingDataSource(about this datasource, can refer to JTA document - http://192.168.18.224:8888/svn/Work_Space/Research/scarlett/booking-service/doc/JTA_lesson_v1.1.doc), Hibernate as the JPA vendor, inject EMF to the DAO to enable JPA.


    The research cases mainly are(refer to the configuration listed in the end of Chapter 3):

    a. EM created from same EMF, under the Hibernate release mode 'auto'.

    For emf, set

    <entry key="hibernate.connection.release_mode" value="auto"/>

    DAO taskHandler use the emf to do testing.

    This is to test under auto release mode, if the connection will be released to connection pool after each statement, if the same EM will use the same connection, if the different EM created by same EMF will use the same connection.


    b. EM created from same EMF, under the Hibernate release mode 'after_transaction' and 'on_close'.

    For emf, set

    <entry key="hibernate.connection.release_mode" value="on_close"/>

    or

    <entry key="hibernate.connection.release_mode" value="after_transaction"/>

    DAO taskHandler use the emf to do testing.

    This is to test under on_close and after_transaction release mode, if the connection will be release to connection pool after each statement, if the same EM can use the same connection, if the different EM created by the same EMF can use the same connection.


    c. EM created from different EMF which referred to same dataSource, under the Hibernate release mode 'auto'.

    For emf, set

    <entry key="hibernate.connection.release_mode" value="auto"/>

    For emf3,set

    <entry key="hibernate.connection.release_mode" value="auto"/>

    DAO taskHandler use the emf,emf3 to do testing, some EM will be created from emf, some EM will be created from emf3.

    This is to test under auto release mode, if the connection will be released to the connection pool after each statement, if the different EM created by different EMF will use the same connection.


    d. EM created from different EMF which referred to same dataSource, under the Hibernate release mode 'after_transaction' and 'on_close'.

    For emf, set

    <entry key="hibernate.connection.release_mode" value="on_close"/>

    or

    <entry key="hibernate.connection.release_mode" value="after_transaction"/>

    For emf3,set

    <entry key="hibernate.connection.release_mode" value="on_close"/>

    or

    <entry key="hibernate.connection.release_mode" value="after_transaction"/>

    DAO taskHandler use the emf,emf3 to do testing, some EM will be created from emf, some EM will be created from emf3.

    This is to test under on_close and after_transaction release mode, if the connection will be release to connection pool after each statement, if the different EM created by different EMF can use the same connection.


    lessons learned from the research above:

    - with connection pool enabled, when client disconnect, the connection to server will not be terminated, the connection manager just change the status of the connection from 'used' to 'free' in the connection pool, server will not be awared of this.


    - when client disconnect, connection released to connection pool, the database session will not be cleared. So next time client picks up the connection again, it will use the same connection and same database session.


    - temp table is only visible to the same connection, so when a temp table created in one connection, and then the connection is released to the connection pool, later client pick up the connection again, it will be able to access the temp table created before.


    - connection is bound to data source, so in JPA, different EMs, if only they refer to the same data source, they will be able to use the same connection.


    - when connection release mode is after_transaction or on_close, in one transaction, the EM will occupy the connection all along, other EM even refer to same data source will not be able to use the connection. When the connection release mode is after_statement, it will make sense to the efficiency, EMs refer to same data source will be able to use the same connection.


    Considering above lessons learned, we have to follow some rules when develop:

    - Even with same EM, we should not assume that the same connection is used if the connection_release mode is after_statement and in concurrent environment.


    - It is NOT a good practice to create temp table and then expect the next function to pick it up in concurrent environment.


    - Should clean up DB resources e.g. temp table before statement finish execute.


    1. JPA And Stored Procedure

    JPA abstracts the persistence layers which use the ORM mechanism to manage the domain objects, objects associations and interaction with the database.


    Stored Procedure(SP) are also commonly used in database application development. It moves some of the operation work from business layer to database layer.


    Using SP can bring some benefits, such as can reduce the client/server data shutting, can avoid duplicate functionality and logic in each program that accesses the data, especially for mass data operation, SP may be a good choice.


      1. How To Use SP With Hibernate/JPA

    JPA supports native query which can be used with SQL or simple SP. It can be used in two ways, dynamic native query and annotation native query.


    5.1.1 Dynamic Native Query

    Dynamic native query is achieved by the createNativeQuery API of EM.


    First we need to create the SP in the database.

    e.g.

    DELIMITER //

    CREATE PROCEDURE CancelBooking(IN bookingNo varchar(20))

    BEGIN

    UPDATE booking SET status = 'X' WHERE booking_no = bookingNo;

    SELECT * FROM booking WHERE booking_no = bookingNo;

    END //

    DELIMITER ;

     

    The first command DELIMITER is used to change the standard delimiter(semicolon) to another, in this case, the delimiter is changed from semicolon(;) to //, so you can have multiple SQL statements in the SP which can be separated by the semicolon. The last command changes the delimiter back to semicolon.


    Then in the DAO, we can use EM's createNativeQuery to execute the SP, the calling grammer is like this.

    e.g.

    Query query = em.createNativeQuery("{call CancelBooking(:bookingNo)}", BookingBasic.class);

    query.setParameter("bookingNo", "booking-000");

    BookingBasic bb = (BookingBasic) query.getResultList().get(0);


    The input parameter is written in the bracket of the procedure, For input parameter setting, we also can use a ? to mark it, then we assign the value by sequence.

    e.g.

    Query query = em.createNativeQuery("{call CancelBooking(?)}", BookingBasic.class);

    query.setParameter(1, "booking-000");

     

    the most convernient way, we can directly set in the query string.

    e.g.

    Query query = em.createNativeQuery("{call CancelBooking('booking-000')}", BookingBasic.class);

     

     

    5.1.2 Annotation Native Query

    JPA also supports to define the name query using annotation under the Entity.

    e.g.

    @Entity

    @NamedNativeQuery(name = "cancelBooking", query = "{call CancelBooking(?)}",

    resultClass = BookingBasic.class,

    hints = {@QueryHint(name = "org.hibernate.callable", value = "true") })

    @Table(name="booking")

    publicclass BookingBasic implements Serializable


    The annotation @NamedNativeQuery is used to annotate a native query with a name “cancelBooking”, when we call named query through EM's API createNamedQuery, we can use this name. query indicates the query string. resultClass defines the result mapping to which class. In the hints, we define the feature org.hibernate.callable, and set to true, by default it is false, we should set to ture, then Hibermate can support JPA's named native query.

     

    Then in the DAO, we can create named query.

    e.g.

    Query q = em.createNamedQuery("cancelBooking");

    q.setParameter(1, "booking-000");

    BookingBasic bb = (BookingBasic) q.getResultList().get(0);

     

    We need to pass the query name to the createNamedQuery API.


    5.2 Choose JPA Or SP

    Direct SQL can be fine-tuned in every aspect, but the backwords, such as lack of portability and maintainability are significant.


    JPA does not well support the stored procedure, we can only use native SQL query for simmple stored procedures, and it is not recommended to use JPA with SP.


    We may also recommend to choose to use JPA in most of the time, it is easy to debug and does not involve much of database. We can concentrate more on the business logic but not the data accessing logic.


  • 相关阅读:
    Java页面中文编码要转换两次encodeURI
    ajax用get刷新页面元素在IE下无效解决~~
    祝贺自己开博~~
    错误域控降级导致解析问题
    ELK 脚本自动化删除索引
    Windows针对子目录共享权限控制
    ES查询区分大小写
    Docker异常时区问题
    Docker自定义镜像无容器日志输出
    ELK时间戳
  • 原文地址:https://www.cnblogs.com/scarlettxu/p/3405343.html
Copyright © 2011-2022 走看看