基于Hibernate的JPA2.0快速构建

前言

尽管现在开源的框架已经非常优秀,但是缺乏统一的标准有违软件开源的初衷,因此Sun公司的JCP组织发布了Java EE的JPA标准,并统一ORM规则、JPQL查询语言、子查询、高级查询和批量处理等操作。推出JPA规范有两点原因:一是,希望简化现有的SE和EE工作,特别是EJB3.0的推出,使得企业级项目的开发推向更高的层次(EJB3.0已经非常优秀,但是由于时间的问题,并且Spring已经占领了市场);二是,希望业界有一个统一的标准,就像当年JDBC推出一样,只实现接口,其余API的实现交给项目厂商或者组织去完成。

Hibernate JPA

实际项目开发应该尽量使用JPA的API编程,为什么要这样做?JPA是一个规范,不是产品,最终实现交给Hibernate这些ORM框架去实现,即使以后我们要改变ORM的底层,也只需要简单更改一下配置即可,JPA还是属于javax.persistence.*的包里面,稍微有些不同的是主键的生成策略不同的ORM规范有所差异,但基本原理还是一样。

下面将分hibernate实现和整合Spring实现两种案例说明JPA整合的好处。

Maven包管理

由于Spring、Hibernate和log4j的maven配置比较繁琐,这里只提供版本管理:

<properties>

    <!--Spring版本号-->
    <spring.version>4.0.5.RELEASE</spring.version>
    <!--hibernate-->
    <hibernate.version>4.3.0.Final</hibernate.version>
    <!--log4j日志文件管理包版本-->
    <slf4j.version>1.6.6</slf4j.version>
    <log4j.version>1.2.12</log4j.version>
    <!--项目编码级别-->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!--spring jpa-->
    <spring-data-jpa.version>1.4.1.RELEASE</spring-data-jpa.version>
    <hibernate-jpa-api.version>1.0.1.Final</hibernate-jpa-api.version>
</properties>

POJO实体

由于使用注解方式创建,因此本文将不创建orm.xml文件,并且JPA的注解也非常容易看懂,因此注解内容不做详述,下面创建一个用户实体对象,对应数据库中的表为t_user;

import javax.persistence.*;
import java.io.Serializable;

/**
 * @author Barudisshu
 */
@Entity(name = "USER")
@Table(name = "t_user",uniqueConstraints = {@UniqueConstraint(columnNames = {"username"})})
public class UserInfo implements Serializable{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // 希望用户名是唯一的
    @Basic
    @Column(name = "username")
    private String username;
    @Basic
    @Column(name = "password")
    private String password;

    // 如果Id生成策略被指定,不应该有带参数的构造函数
    // 省略getter和setter方法
}

因为@Id中的Id字段是唯一的,如果生成策略已经指定,使用persist(Object obj)方法时将抛出异常,因此,要么改为merge方法,要么去掉@GeneratedVale注解,因为persist会寻找游离态的实体是根据Id进行标识,如果没有则自行创建。

persistence.xml

默认persistence.xml文件处于根目录的META-INF文件下,并且名称固定,如果使用想使用Maven指定,需要配置ant插件:

<plugins>
    <!--如果想指定persistence.xml位置,则添加如下插件-->
    <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.3</version>
        <executions>
            <execution>
                <id>copy-test-persistence</id>
                <phase>process-test-resources</phase>
                <configuration>
                    <tasks>
                        <!--backup the "proper" persistence.xml-->
                        <copy file="${project.build.outputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml.proper"/>
                        <!--replace the "proper" persistence.xml with the "test" version-->
                        <copy file="${project.build.testOutputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>
                    </tasks>
                </configuration>
                <goals>
                    <goal>run</goal>
                </goals>
            </execution>
            <execution>
                <id>restore-persistence</id>
                <phase>prepare-package</phase>
                <configuration>
                    <tasks>
                        <!--restore the "proper" persistence.xml-->
                        <copy file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>
                    </tasks>
                </configuration>
                <goals>
                    <goal>run</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
</plugins>

OK,前面已经说过,只使用注解方式,不使用xml方式,所以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">

    <!--持久化单元,使用JTA必须架构在JBOSS服务器上-->
    <persistence-unit name="person_pu" transaction-type="RESOURCE_LOCAL">

        <!--hibernate jpa提供者为HibernatePersistenceProvider-->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

        <!--持久化对象-->
        <class>ls.jpa.entity.UserInfo</class>

        <properties>
            <!--基本属性-->
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/db_test"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="***"/>
            <!--hibernate配置-->
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

注意:JPA2.0和JPA1.0有较大差别,JPA2.0是提供了类型安全的查询机制、丰富的表达式等功能,并且已经从EJB中独立开来。

单元测试

单元测试类不用显式加载persistence.xml文件,到底persistence.xml文件是如何加载的为什么名称是固定的?笔者找了很久,原来是是在org.hibernate.jpa.boot.internal.PersistenceXmlParser类下定义的,如果单纯从Persistence类下是找不到的,因为hibernate.jpa.boot包下会加载所有配置文件并转换为properties属性存储在Map中,然后通过工具类或者通过ClassLoader进行属性访问。下面为单元测试代码:

import ***;

/**
 * @author Barudisshu
 */
public class LogonTest {

    private static final Logger logger = Logger.getLogger(LogonTest.class);

    private static final String PERSISTENCE_UNIT_NAME = "person_pu";

    @Test
    public void simpleTests(){
        // Obtaining an entity manager
        EntityManagerFactory factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
        EntityManager entityManager = factory.createEntityManager();
        // Read the existing entries and write to console
        TypedQuery<UserInfo> query = entityManager.createQuery("select u from USER u", UserInfo.class);
        List<UserInfo> userList = query.getResultList();

        // List user name
        for (UserInfo userInfo : userList) {
            logger.info(userInfo.getUsername());
        }

        logger.info("Size: " + userList.size());

        // Simple transaction
        entityManager.getTransaction().begin();
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("Barudisshu");
        userInfo.setPassword("88888888");
        entityManager.persist(userInfo);
        entityManager.getTransaction().commit();

        // Close the EM and EMF when done
        entityManager.close();
        factory.close();

    }

}

Spring集成

单纯使用JPA很难满足业务上的需求,因此可以通过Spring进行集成,进而架构成为逻辑清晰的业务层次。首先添加相应的Maven管理包,如下:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>${spring.version}</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>${spring.version}</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${spring.version}</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>${spring.version}</version>
</dependency>

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>${spring-data-jpa.version}</version>
</dependency>

为了达到分层的架构,一下分别添加Dao层和Service层,代码清单如下:

Dao层,自动装配持久化上下文:

import ***;

/**
 * @author Barudisshu
 */
@Repository
public class UserDao {

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public UserInfo save(UserInfo userInfo){
        em.persist(userInfo);
        return userInfo;
    }
}

Service层,创建服务:

import ***;

/**
 * @author Barudisshu
 */
@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    @Transactional
    public UserInfo create(String name,String password){

        UserInfo userInfo = new UserInfo();
        userInfo.setUsername(name);
        userInfo.setPassword(password);

        return userDao.save(userInfo);
    }
}

上述代码没有使用JpaTemplate模版,自spring3.1之后就没有了,可能是JPA规范做得足够好的原因,就像自spring3.2.x之后不再支持HibernateTemplate一样。

Spring整合Hibernate时,Spring可以通过容器来管理Hibernate的SessionFactory;类似地,Spring整合JPA时,Spring可以通过容器管理JPA的EntityManagerFactory。Spring为JPA EntityManagerFactory管理提供了两种方式:

  1. LocalEntityManagerFactoryBean
  2. LocalContainerEntityManagerFactoryBean

LocalEntityMangerFactoryBean功能比较有限,只能通过persistence.xml内容的属性构建,因此不能使用Spring容器中已有的DataSource,也不能切换全局事务。而LocalContainerEntityManagerFactoryBean则刚好弥补了这方面的不足,并且可以使用Spring容器中已有的数据源,这样persistence.xml就不用编写任何数据源了。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="ls.jpa"/>

    <bean id="emf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
        <!--注入JPA持久化单元-->
        <property name="persistenceUnitName" value="person_pu"/>
    </bean>

    <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="emf"/>
    </bean>

    <!--开启AOP监听-->
    <aop:aspectj-autoproxy expose-proxy="true"/>

    <!--使用声明式事务-->
    <tx:annotation-driven transaction-manager="txManager"/>

</beans>

OK,所有配置已经完成,下面进行简单的单元测试:

import ***;

/**
 * @author Barudisshu
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-config.xml"})
@Transactional
@TransactionConfiguration(transactionManager = "txManager",defaultRollback = true)
public class SimpleTest {

    private static final Logger logger = Logger.getLogger(SimpleTest.class);

    @Autowired
    private UserService userService;

    @Test
    public void userTests(){
        UserInfo userInfo = userService.create("Barudisshu","liter");
        logger.info(JSON.toJSONString(userInfo));
    }
}

单元测试绿条通过后,打开数据库可以看到,已经自动为你创建t_user用户表:

Well done!

时间: 2024-07-31 00:30:43

基于Hibernate的JPA2.0快速构建的相关文章

Docker + SpringBoot2.0快速构建镜像

博文链接 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,也可以实现虚拟化.容器是完全使用沙箱机制,相互之间不会有任何接口.(引用官方概念,本文注重docker+springboot2.0快速构建镜像,对docker更细节的知识不在做赘述) Docker 本文使用的是CentOs7 docker 安装: yum install docker systemctl start docker 开

vue2.0快速构建项目

准备工作:已经安装了nodejs,已经安装了vue-cli $ mkdir gankbook $ cd gankbook $ vue init webpack-simple 按照需要写好信息,这将会写在package.json里面去的 创建完好之后运行起来 先下载npm包 $ npm install //运行 $ npm run dev //发布 $ npm run build //还需要安装vue-router  vue-resource 这里用了淘宝的镜像,避免安装失败,当然前提还是你得先安

基于 Docker 快速构建 Linux 0.11 实验环境

by Falcon of TinyLab.org 2015/05/02 简介 五分钟内搭建 Linux 0.11 的实验环境介绍了如何快速构建一个 Linux 0.11 实验环境. 本文介绍如何快速构建一个独立于宿主机的 Linux 0.11 实验环境,该实验环境可以用于任何操作系统的宿主开发机,将非常方便各类学生学习 Linux 0.11,本文只介绍 Ubuntu.在 Windows 和 Mac 下可以用 VirtualBox + Boot2Docker 来启动. 下文要求已经安装 git 和

Centos6.4下快速构建基于用户名密码验证的openvpn服务器

如今VPN应用已经非常广泛了,之前就写过一篇关于PPTPD的博文.链接是http://cyr520.blog.51cto.com/714067/1161788. 家里最近新换了一个宽带.居然没办法用拨上公司的PptpdVPN,一直拨号状态一直停留在正在验证用户名和密码这一步.百度谷歌了一大圈,也没解决这个问题.貌似是因为宽带运营商屏蔽GRE协议的原因.作为运维人员,不能及时的连上公司的内部网络是一件很让人不爽的事情. 干脆再搞一套OpenVPN吧,据说OpenVPN非常强大,可以适应很复杂的网络

基于Salt Master/Minions快速构建Salt SSH环境

前置阅读 环境说明 开工 创建用于Salt SSH环境的用户及key认证管理环境 配置Mine,以获取Minion id及IP地址 生成Salt Rosters 应用Salt SSH 后话 Salt 0.17版本已发布,该版本中重要的特性是引入了Salt SSH系统,提供了无需Minion.基于SSH的维护方式.原有的Salt维护环境已经初具规模,再手动重新构建Salt SSH环境成本较高.偷懒是人的天性,利用原有SaltStack Master/Minions环境,如何快速构建新的Salt S

基于Bluemix快速构建部署一款Java小程序——微博影响力分析器

一.前言 近年来IT界风云际会,各种新概念如大数据.云平台.虚拟化等,如雨后春笋般层出不穷.而云平台,可以说是近些年来引领时代新潮的边缘概念之一,各大厂商竞相推出各种云产品,抢占云市场高地.近期,IT百年老厂IBM也推出了其花费重金打造的基于CloudFoundry开放标准的云平台Bluemix.本文就旨在从一位普通Java开发者角度,尝鲜Bluemix云平台及其提供的Cloudant服务,快速构建并部署一款Java小程序"微博影响力分析器". 二."微博影响力分析器&quo

快速构建一个 Springboot

快速构建一个 Springboot 官网:http://projects.spring.io/spring-boot/ Spring Boot可以轻松创建可以"运行"的独立的,生产级的基于Spring的应用程序.我们对Spring平台和第三方图书馆有一个看法,所以你可以从最开始的时候开始吧.大多数Spring Boot应用程序需要很少的Spring配置. 特征 创建独立的Spring应用程序 直接嵌入Tomcat,Jetty或Undertow(不需要部署WAR文件) 提供有意思的&qu

[原创]利用system verilog快速构建单元仿真

在一些单元模块仿真时,往往需要构建一定格式的数据激励,如某个处理TCP报文的单元模块,需要构建符合TCP报文格式的激励.基于verilog的激励生成,大致有两种方法: txt文件法.将符合需求的数据记录于txt,仿真时调用. 直接合成法.利用verilog在tb中直接合成激励. 这两种方法的优点是直观,但不够灵活.其一,当激励的数据结构复杂时,构建起来比较麻烦:其二,当被测对象的输入协议改动时,往往牵一发而动全身,需要对tb做整体的检查. 利用system verilog构建单元测试可以克服上述

快速构建框架(S2SH)

搭建项目:1.创建web项目2.引入类库    [struts2]        asm-3.3.jar        asm-commons-3.3.jar        asm-tree-3.3.jar        commons-fileupload-1.3.1.jar        commons-io-2.2.jar        commons-lang3-3.1.jar        freemarker-2.3.19.jar        ognl-3.0.6.jar