spring 3.0 + jpa + hibernate 的实现步骤,附完整工程包
2012-02-18 13:33
513 查看
工程包下载地址 : 点此下载
1. 在web.xml里面加入 spring支持。
[xhtml] view plaincopy
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>spring-jpa-hibernate</display-name>
<!-- 定义spring配置 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/META-INF/applicationContext.xml</param-value>
</context-param>
<!-- 定义结束 -->
<!-- 定义本项目的系统变量读取程序 -->
<listener>
<listener-class>com.alcor.test.configer.ApplicationListener</listener-class>
</listener>
<!-- 定义结束 -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app
注意其中关键代码段:
<!-- 定义spring配置 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/META-INF/applicationContext.xml</param-value>
</context-param>
<!-- 定义结束 -->
2.在src的META-INF目录下建立applicationContext.xml文件
[xhtml] view plaincopy
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.alcor.test.service" /> <!-- 扫描规则 -->
<import resource="jpa.xml"/>
<!-- AOP部分定义 -->
</beans>
3.在src的META-INF目录下建立jpa.xml文件
[xhtml] view plaincopy
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<!-- entityManagerFactory 有以下2种方式提供 -->
<!-- class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">-->
<!-- class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="Test_PU" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
4.在src的META-INF目录下建立persistence.xml文件
[xhtml] view plaincopy
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="Test_PU" transaction-type="RESOURCE_LOCAL">
<!-- provider>org.eclipse.persistence.jpa.PersistenceProvider</provider-->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- MYSql 的连接-->
<properties>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/tester?useUnicode=true&characterEncoding=utf-8" />
<property name="hibernate.connection.username" value="tester" />
<property name="hibernate.connection.password" value="tester" />
<property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>
<property name="hibernate.c3p0.max_size" value="100"/>
<property name="hibernate.c3p0.min_size" value="20"/>
<property name="hibernate.c3p0.timeout" value="120"/>
<property name="hibernate.c3p0.max_statements" value="0"/>
<property name="hibernate.c3p0.idle_test_period" value="120"/>
<property name="hibernate.c3p0.acquire_increment" value="5 "/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/>
<property name="hibernate.cache.use_query_cache" value="false"/>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.useUnicode" value="true"/>
<property name="hibernate.characterEncoding" value="utf8"/>
</properties>
</persistence-unit>
</persistence>
5.建立TestaService.java,实现了findbyID 和add方法以及一个JPQL的用法。一个是有事务提交的。其他2个是没有事务提交,查询的
[java] view plaincopy
package com.alcor.test.service;
import java.util.List;
import java.util.UUID;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.alcor.test.pojo.Testa;
import com.alcor.test.pojo.TestaPK;
import com.alcor.test.pojo.Testb;
@Transactional
@Service("com.alcor.test.service.TestaService")
public class TestaService {
/**
* Logger for this class
*/
private static final Logger logger = Logger.getLogger(TestaService.class);
@Autowired
private EchoService echoService;
@PersistenceContext
EntityManager em;
@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
public Testa findByID(TestaPK id) {
if (logger.isDebugEnabled()) {
logger.debug("findByID(TestaPK) - start"); //$NON-NLS-1$
}
logger.debug(id.getId()+"|"+id.getName());
Testa returnTesta = em.find(Testa.class, id);
if (logger.isDebugEnabled()) {
logger.debug("findByID(TestaPK) - end"); //$NON-NLS-1$
}
return returnTesta;
}
public void add(){
if (logger.isDebugEnabled()) {
logger.debug("add() - start"); //$NON-NLS-1$
}
//保存A表
Testa testa = new Testa();
TestaPK testaPK = new TestaPK();
testaPK.setId(UUID.randomUUID().toString());
testaPK.setName(UUID.randomUUID().toString());
testa.setId(testaPK);
em.persist(testa);
//保存B表
Testb testb = new Testb();
testb.setId(UUID.randomUUID().toString());
em.persist(testb);
//调用一个autowired 的service
echoService.doNothing();
if (logger.isDebugEnabled()) {
logger.debug("add() - end"); //$NON-NLS-1$
}
}
/**
* 通过使用JPQL 来做查询
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
public List<Testa> findAllBySex (){
if (logger.isDebugEnabled()) {
logger.debug("findAllBySex() - start"); //$NON-NLS-1$
}
String queryString = "SELECT a FROM Testa a WHERE a.age < :age AND a.id.name like :name";
Query query = em.createQuery(queryString);
query.setParameter("name", "%xv%");
query.setParameter("age", 20);
List<Testa> results = query.getResultList();
if (logger.isDebugEnabled()) {
logger.debug("findAllBySex() - end"); //$NON-NLS-1$
}
return results;
}
}
6.建立测试用例
[java] view plaincopy
package com.alcor.test.junit;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.apache.log4j.Logger;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import com.alcor.test.pojo.TestaPK;
import com.alcor.test.service.TestaService;
public class TestUnit extends TestCase {
/**
* Logger for this class
*/
private static final Logger logger = Logger.getLogger(TestUnit.class);
private static TestaService testaService ;
public TestUnit(String testName) {
super(testName);
}
@Test
public void testTransaction() throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("testTransaction() - start"); //$NON-NLS-1$
}
try{
testaService.add();
}catch (Exception err){
logger.debug(err.getMessage(),err);
}
if (logger.isDebugEnabled()) {
logger.debug("testTransaction() - end"); //$NON-NLS-1$
}
}
public void getPOJO ()throws Exception{
if (logger.isDebugEnabled()) {
logger.debug("getPOJO() - start"); //$NON-NLS-1$
}
TestaPK id = new TestaPK();
id.setId("1");
id.setName("1");
logger.debug(testaService.findByID(id).getAge());
if (logger.isDebugEnabled()) {
logger.debug("getPOJO() - end"); //$NON-NLS-1$
}
}
public static junit.framework.Test suite() {
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:/My Documents/project/spring-jpa-hibernate/src/META-INF/applicationContext.xml");
TestUnit.testaService= (TestaService)ctx.getBean("com.alcor.test.service.TestaService");
TestSuite suite = new TestSuite("test DBService");
//suite.addTest(new TestUnit("testTransaction"));
suite.addTest(new TestUnit("getPOJO"));
return suite;
}
}
此测试用例仅仅是测试java app。非web应用。
附:JPQL的介绍
一、什么是JPQL
在 Java EE 中,JPQL( Java 持久性查询语言)是专门为Java 应用程序访问和导航实体实例设计的。JPQL是EJB2使用的查询语言EJB QL的扩展,它继承了EJB QL并对其做了一些改变。
二、JPQL与SQL
JPQL 和 SQL 有很多相似之处。归根结底,它们都用于访问和操作数据库数据。而且,二者都使用非过程语句 — 通过特殊解释程序识别的命令。此外,JPQL 在语法上与 SQL 也相似。
JPQL 和 SQL 的主要区别在于,前者处理 JPA 实体,后者直接在数据库空间内对表、列、行等关系数据进行处理。
三、使用JPQL
要从 Java 代码内发出 JPQL 查询,您需要利用 EntityManager API 和 Query API 的相应方法,执行以下一般步骤:
1. 使用注入或通过 EntityManagerFactory 实例获取一个 EntityManager 实例。
2. 通过调用相应 EntityManager 的方法(如 createQuery),创建一个 Query 实例。
3. 如果有查询参数,使用相应 Query 的 setParameter 方法进行设置。
4. 如果需要,使用 setMaxResults 和/或 setFirstResult Query 的方法设置要检索的实例的最大数量和/或指定检索的起始实例位置。
5. 如果需要,使用 setHint Query 的方法设置供应商特定的提示。
6. 如果需要,使用 setFlushMode Query 的方法设置查询执行的刷新模式,覆盖实体管理器的刷新模式。
7. 使用相应 Query 的方法 getSingleResult 或 getResultList 执行查询。如果进行更新或删除操作,您必须使用 executeUpdate 方法,它返回已更新或删除的实体实例的数量。
JPQL的查询可以分为命名查询和动态查询。
动态查询
可以使用EntityManager.createQuery方法创建动态查询,唯一的要求是把合法的JPQL语句传递给此方法。如下:
Query query = em.createQuery(“select p from Person p where p.id=1033”);
其中where语句可是可选的。在这里JPQL看上去和SQL语句很像,但应当注意到的是from后边的Person是实体Bean而不是数据表。
在所写的JPQL语句中你可以像示例中那样的直接将查询条件写在语句中。但是还有更好的方法。在这里你可以使用设置查询参数的方式,其中又有位置参数和命名参数的分别。
使用位置参数如下所示:
Query query = em.createQuery(“select p from Person p where p.id=?1”);
Query.setParameter(1, 1033);//第一个参数是位置,第二个参数查询条件
使用命名参数如下所示:
Query query = em.createQuery(“select p from Person p where p.id=:id”);
Query.setParameter(“id”, 1033);//第一个参数是参数名称,第二个参数查询条件
需要注意的是位置参数的是位置前加符号”?”,命名参数是名称前是加符号”:”。
如果你需要传递java.util.Date或java.util.Calendar参数进一个参数查询,你需要使用一个特殊的setParameter()方法。因为一个Date或Calendar对象能够描述一个真实的日期、时间或时间戳.所以我们需要告诉Query对象怎么使用这些参数,我们把javax.persistence.TemporalType作为参数传递进setParameter方法,告诉查询接口在转换java.util.Date或java.util.Calendar参数到本地SQL时使用什么数据库类型。
查询结果
使用Query. getSingleResult方法得到查询的单个实例,返回Object。要确保使用此方法时查询只检索到一个实体。
使用Query. getResultList方法得到查询的实例集合,返回List。
通常的,我们会如示例中所示的是获取查询返回的是实体,但是在JPQL里我们也可以得到实体的部分属性,就如同使用SQL得到表中的部分列一样。如果是获取部分属性的话,Query.getResultList方法返回的会是Object数组的List每个Object数组项相当于是一条结果,数组的成员是属性值,顺序和所写的JPQL中的SELECT中所写顺序一致。
查询中使用构造器(Constructor)
可以在SELECT子句中使用构造器返回一个或多个java实例。如下所示:
Query query = em.createQuery("select new com.demo.bean.Person(p.id, p.name) from Person p order by p.id desc");
查询分页
JPA提供了在结果集合中使用分页的功能,使用这个功能我们可以轻松的达到对查询结果分页的目的。如下
Query.setMaxResults(10);//设置分页大小,在这里为每页10条记录
Query.setFirstResult(10);//指定第一个结果的位置。这里是指定第11条作为起始结果。
这里只要在setFirstResult中使用动态参数即可方便的对结果进行分页了。
使用操作符
在where子句中我们可以使用一些操作符来进行条件的选择。
NOT操作符
select p from Person p where not(p.id = 1036)
//查询id不是1036的所有人
BETWEEN操作符
select p from Person p where p.age between 20 and 26
//查询年龄在20到26的所有人;上限和下限必须是相同的数据类型
IS NULL操作符
select p from Person p where p.name is not null
//查询名字不为NULL的所有人
IS EMPTY操作符
IS EMPTY是针对集合属性(Collection)的操作符。可以和NOT 一起使用。注:低版权的Mysql 不支持IS EMPTY
select p from Person p where p.interest is empty
//查询兴趣(是集合)是空的所有人
IN 操作符
select p from Person p where p.id in (101,102)
//查询id是101和102的人
EXISTS 操作符
[NOT]EXISTS 需要和子查询配合使用。注:低版权的Mysql 不支持EXISTS
select p from Person p where exists (select b from Book b where b.name like ‘%EJB%’ )
//如果书籍名字中包含有EJB字样,则得到所有人
MEMBER OF操作符
可以使用MEMBER OF操作符检查集合-值路径表达式中是否存在标识符变量、单一值表达式或是输入参数。
select p from Person p where :interest member of p.interest
//查询兴趣(是集合)是中包含所输入的兴趣实体实例(:interest)的所有人
使用字符串函数
JPQL定义了内置函数方便使用。这些函数的使用方法和SQL中相应的函数方法类似。包括:
1. CONCAT 字符串拼接
2. SUBSTRING 字符串截取
3. TRIM 去掉空格
4. LOWER 转换成小写
5. UPPER 装换成大写
6. LENGTH 字符串长度
7. LOCATE 字符串定位
使用算术函数
JPQL仅仅支持了最低限度的算术函数集合,可以在JPQL的where和having子句中使用算术函数。JPQL定义的算术函数包括:
ABS 绝对值
SQRT 平方根
MOD 取余数
SIZE 取集合的数量
使用时间函数
JPQL像大多数语言一样提供了获得当前日期、时间或是时间标记的函数。这些函数转换为数据库专有的SQL函数,从数据库检索当前日期、时间或时间标记。包含的时间函数如下:
CURRENT_DATE 返回当前日期
CURRENT_TIME 返回当前时间
CURRENT_TIMESTAMP 返回当前时间标记
联结(连接/关联)
JPQL仍然支持和SQL中类似的关联语法:
left out join/left join
left out join/left join等,都是允许符合条件的右边表达式中的Entities 为空(需要显式使用left join/left outer join 的情况会比较少。)
inner join
inner join 要求右边的表达式必须返回Entities。
left join fetch/inner join fetch
在默认的查询中,Entity中的集合属性默认不会被关联,集合属性默认是延迟加载( lazy-load )。那么,left fetch/left out fetch/inner join fetch提供了一种灵活的查询加载方式来提高查询的性能(集合属性被关联,同Entity同时加载而不是在需要时再加载,这样就转换为SQL语句时为一条SQL语句,而不是加载Entity时一条语句,加载集合属性时有N(等于Entity数量)条语句,避免了N+1问题,提高了查询性能)。
使用聚合函数
JPQL提供的聚合函数有AVG、COUNT、MAX、MIN、SUM。在AVG、MAX、MIN、SUM函数中只能使用持久化字段,而在COUNT中可以使用任何类型的路径表达式或标识符。
COUNT返回的类型是Long,AVG是Double,SUM可能返回Long或Double。
分组
如果聚合函数不是select...from的唯一一个返回列,需要使用"GROUP BY"语句。"GROUP BY"应该包含select 语句中除了聚合函数外的所有属性。如果还需要加上查询条件,需要使用"HAVING"条件语句而不是"WHERE"语句
select p.grade, count(p) from Person p where p.age > 20 group by p.grade having count(*)>120
//返回年龄大于20的各年级的总人数(人数大于120)
排序
在JPQL中像SQL一样使用order by 来进行排序。"ASC"和"DESC"分别为升序和降序,JPQL中默认为ASC升序。
批删除和批更新
JPQL支持批量删除和批量更新的操作。和查询相同的是也是要使用EntityManager.createQuery方法来创建一个Query实例,不同的是在这里要使用Query.executeUpdate方法来直行删除和更新,该方法返回的值是操作的记录数。
命名查询
可以在实体bean上通过@NamedQuery or @NamedQueries预先定义一个或多个查询语句,减少每次因书写错误而引起的BUG。通常把经常使用的查询语句定义成命名查询。
定义单个命名查询:
@NamedQuery(name="getPerson", query= "select p from Person p where p.id=?1")
@Entity
public class Person implements Serializable{
如果要定义多个命名查询,应在@javax.persistence.NamedQueries里定义@NamedQuery:
@NamedQueries({
@NamedQuery(name="getPerson", query= "select p from Person p where p.id=?1"),
@NamedQuery(name="getPersonList", query= "select p from Person as p where p.age>?1")
})
@Entity
public class Person implements Serializable{
当命名查询定义好了之后,我们就可以通过名称执行其查询。代码如下:
Query query = em.createNamedQuery("getPerson");
query.setParameter(1, 1);
四、注意
1. JPQL语句的大小写敏感性:除了Java 类和属性名称外,查询都是大小写不敏感的。
2. 使用参数查询的时候除了基本类型的参数还可以使用实体参数。
1. 在web.xml里面加入 spring支持。
[xhtml] view plaincopy
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>spring-jpa-hibernate</display-name>
<!-- 定义spring配置 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/META-INF/applicationContext.xml</param-value>
</context-param>
<!-- 定义结束 -->
<!-- 定义本项目的系统变量读取程序 -->
<listener>
<listener-class>com.alcor.test.configer.ApplicationListener</listener-class>
</listener>
<!-- 定义结束 -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app
注意其中关键代码段:
<!-- 定义spring配置 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/META-INF/applicationContext.xml</param-value>
</context-param>
<!-- 定义结束 -->
2.在src的META-INF目录下建立applicationContext.xml文件
[xhtml] view plaincopy
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.alcor.test.service" /> <!-- 扫描规则 -->
<import resource="jpa.xml"/>
<!-- AOP部分定义 -->
</beans>
3.在src的META-INF目录下建立jpa.xml文件
[xhtml] view plaincopy
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<!-- entityManagerFactory 有以下2种方式提供 -->
<!-- class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">-->
<!-- class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="Test_PU" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
4.在src的META-INF目录下建立persistence.xml文件
[xhtml] view plaincopy
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="Test_PU" transaction-type="RESOURCE_LOCAL">
<!-- provider>org.eclipse.persistence.jpa.PersistenceProvider</provider-->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- MYSql 的连接-->
<properties>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/tester?useUnicode=true&characterEncoding=utf-8" />
<property name="hibernate.connection.username" value="tester" />
<property name="hibernate.connection.password" value="tester" />
<property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>
<property name="hibernate.c3p0.max_size" value="100"/>
<property name="hibernate.c3p0.min_size" value="20"/>
<property name="hibernate.c3p0.timeout" value="120"/>
<property name="hibernate.c3p0.max_statements" value="0"/>
<property name="hibernate.c3p0.idle_test_period" value="120"/>
<property name="hibernate.c3p0.acquire_increment" value="5 "/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/>
<property name="hibernate.cache.use_query_cache" value="false"/>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.useUnicode" value="true"/>
<property name="hibernate.characterEncoding" value="utf8"/>
</properties>
</persistence-unit>
</persistence>
5.建立TestaService.java,实现了findbyID 和add方法以及一个JPQL的用法。一个是有事务提交的。其他2个是没有事务提交,查询的
[java] view plaincopy
package com.alcor.test.service;
import java.util.List;
import java.util.UUID;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.alcor.test.pojo.Testa;
import com.alcor.test.pojo.TestaPK;
import com.alcor.test.pojo.Testb;
@Transactional
@Service("com.alcor.test.service.TestaService")
public class TestaService {
/**
* Logger for this class
*/
private static final Logger logger = Logger.getLogger(TestaService.class);
@Autowired
private EchoService echoService;
@PersistenceContext
EntityManager em;
@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
public Testa findByID(TestaPK id) {
if (logger.isDebugEnabled()) {
logger.debug("findByID(TestaPK) - start"); //$NON-NLS-1$
}
logger.debug(id.getId()+"|"+id.getName());
Testa returnTesta = em.find(Testa.class, id);
if (logger.isDebugEnabled()) {
logger.debug("findByID(TestaPK) - end"); //$NON-NLS-1$
}
return returnTesta;
}
public void add(){
if (logger.isDebugEnabled()) {
logger.debug("add() - start"); //$NON-NLS-1$
}
//保存A表
Testa testa = new Testa();
TestaPK testaPK = new TestaPK();
testaPK.setId(UUID.randomUUID().toString());
testaPK.setName(UUID.randomUUID().toString());
testa.setId(testaPK);
em.persist(testa);
//保存B表
Testb testb = new Testb();
testb.setId(UUID.randomUUID().toString());
em.persist(testb);
//调用一个autowired 的service
echoService.doNothing();
if (logger.isDebugEnabled()) {
logger.debug("add() - end"); //$NON-NLS-1$
}
}
/**
* 通过使用JPQL 来做查询
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
public List<Testa> findAllBySex (){
if (logger.isDebugEnabled()) {
logger.debug("findAllBySex() - start"); //$NON-NLS-1$
}
String queryString = "SELECT a FROM Testa a WHERE a.age < :age AND a.id.name like :name";
Query query = em.createQuery(queryString);
query.setParameter("name", "%xv%");
query.setParameter("age", 20);
List<Testa> results = query.getResultList();
if (logger.isDebugEnabled()) {
logger.debug("findAllBySex() - end"); //$NON-NLS-1$
}
return results;
}
}
6.建立测试用例
[java] view plaincopy
package com.alcor.test.junit;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.apache.log4j.Logger;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import com.alcor.test.pojo.TestaPK;
import com.alcor.test.service.TestaService;
public class TestUnit extends TestCase {
/**
* Logger for this class
*/
private static final Logger logger = Logger.getLogger(TestUnit.class);
private static TestaService testaService ;
public TestUnit(String testName) {
super(testName);
}
@Test
public void testTransaction() throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("testTransaction() - start"); //$NON-NLS-1$
}
try{
testaService.add();
}catch (Exception err){
logger.debug(err.getMessage(),err);
}
if (logger.isDebugEnabled()) {
logger.debug("testTransaction() - end"); //$NON-NLS-1$
}
}
public void getPOJO ()throws Exception{
if (logger.isDebugEnabled()) {
logger.debug("getPOJO() - start"); //$NON-NLS-1$
}
TestaPK id = new TestaPK();
id.setId("1");
id.setName("1");
logger.debug(testaService.findByID(id).getAge());
if (logger.isDebugEnabled()) {
logger.debug("getPOJO() - end"); //$NON-NLS-1$
}
}
public static junit.framework.Test suite() {
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:/My Documents/project/spring-jpa-hibernate/src/META-INF/applicationContext.xml");
TestUnit.testaService= (TestaService)ctx.getBean("com.alcor.test.service.TestaService");
TestSuite suite = new TestSuite("test DBService");
//suite.addTest(new TestUnit("testTransaction"));
suite.addTest(new TestUnit("getPOJO"));
return suite;
}
}
此测试用例仅仅是测试java app。非web应用。
附:JPQL的介绍
一、什么是JPQL
在 Java EE 中,JPQL( Java 持久性查询语言)是专门为Java 应用程序访问和导航实体实例设计的。JPQL是EJB2使用的查询语言EJB QL的扩展,它继承了EJB QL并对其做了一些改变。
二、JPQL与SQL
JPQL 和 SQL 有很多相似之处。归根结底,它们都用于访问和操作数据库数据。而且,二者都使用非过程语句 — 通过特殊解释程序识别的命令。此外,JPQL 在语法上与 SQL 也相似。
JPQL 和 SQL 的主要区别在于,前者处理 JPA 实体,后者直接在数据库空间内对表、列、行等关系数据进行处理。
三、使用JPQL
要从 Java 代码内发出 JPQL 查询,您需要利用 EntityManager API 和 Query API 的相应方法,执行以下一般步骤:
1. 使用注入或通过 EntityManagerFactory 实例获取一个 EntityManager 实例。
2. 通过调用相应 EntityManager 的方法(如 createQuery),创建一个 Query 实例。
3. 如果有查询参数,使用相应 Query 的 setParameter 方法进行设置。
4. 如果需要,使用 setMaxResults 和/或 setFirstResult Query 的方法设置要检索的实例的最大数量和/或指定检索的起始实例位置。
5. 如果需要,使用 setHint Query 的方法设置供应商特定的提示。
6. 如果需要,使用 setFlushMode Query 的方法设置查询执行的刷新模式,覆盖实体管理器的刷新模式。
7. 使用相应 Query 的方法 getSingleResult 或 getResultList 执行查询。如果进行更新或删除操作,您必须使用 executeUpdate 方法,它返回已更新或删除的实体实例的数量。
JPQL的查询可以分为命名查询和动态查询。
动态查询
可以使用EntityManager.createQuery方法创建动态查询,唯一的要求是把合法的JPQL语句传递给此方法。如下:
Query query = em.createQuery(“select p from Person p where p.id=1033”);
其中where语句可是可选的。在这里JPQL看上去和SQL语句很像,但应当注意到的是from后边的Person是实体Bean而不是数据表。
在所写的JPQL语句中你可以像示例中那样的直接将查询条件写在语句中。但是还有更好的方法。在这里你可以使用设置查询参数的方式,其中又有位置参数和命名参数的分别。
使用位置参数如下所示:
Query query = em.createQuery(“select p from Person p where p.id=?1”);
Query.setParameter(1, 1033);//第一个参数是位置,第二个参数查询条件
使用命名参数如下所示:
Query query = em.createQuery(“select p from Person p where p.id=:id”);
Query.setParameter(“id”, 1033);//第一个参数是参数名称,第二个参数查询条件
需要注意的是位置参数的是位置前加符号”?”,命名参数是名称前是加符号”:”。
如果你需要传递java.util.Date或java.util.Calendar参数进一个参数查询,你需要使用一个特殊的setParameter()方法。因为一个Date或Calendar对象能够描述一个真实的日期、时间或时间戳.所以我们需要告诉Query对象怎么使用这些参数,我们把javax.persistence.TemporalType作为参数传递进setParameter方法,告诉查询接口在转换java.util.Date或java.util.Calendar参数到本地SQL时使用什么数据库类型。
查询结果
使用Query. getSingleResult方法得到查询的单个实例,返回Object。要确保使用此方法时查询只检索到一个实体。
使用Query. getResultList方法得到查询的实例集合,返回List。
通常的,我们会如示例中所示的是获取查询返回的是实体,但是在JPQL里我们也可以得到实体的部分属性,就如同使用SQL得到表中的部分列一样。如果是获取部分属性的话,Query.getResultList方法返回的会是Object数组的List每个Object数组项相当于是一条结果,数组的成员是属性值,顺序和所写的JPQL中的SELECT中所写顺序一致。
查询中使用构造器(Constructor)
可以在SELECT子句中使用构造器返回一个或多个java实例。如下所示:
Query query = em.createQuery("select new com.demo.bean.Person(p.id, p.name) from Person p order by p.id desc");
查询分页
JPA提供了在结果集合中使用分页的功能,使用这个功能我们可以轻松的达到对查询结果分页的目的。如下
Query.setMaxResults(10);//设置分页大小,在这里为每页10条记录
Query.setFirstResult(10);//指定第一个结果的位置。这里是指定第11条作为起始结果。
这里只要在setFirstResult中使用动态参数即可方便的对结果进行分页了。
使用操作符
在where子句中我们可以使用一些操作符来进行条件的选择。
NOT操作符
select p from Person p where not(p.id = 1036)
//查询id不是1036的所有人
BETWEEN操作符
select p from Person p where p.age between 20 and 26
//查询年龄在20到26的所有人;上限和下限必须是相同的数据类型
IS NULL操作符
select p from Person p where p.name is not null
//查询名字不为NULL的所有人
IS EMPTY操作符
IS EMPTY是针对集合属性(Collection)的操作符。可以和NOT 一起使用。注:低版权的Mysql 不支持IS EMPTY
select p from Person p where p.interest is empty
//查询兴趣(是集合)是空的所有人
IN 操作符
select p from Person p where p.id in (101,102)
//查询id是101和102的人
EXISTS 操作符
[NOT]EXISTS 需要和子查询配合使用。注:低版权的Mysql 不支持EXISTS
select p from Person p where exists (select b from Book b where b.name like ‘%EJB%’ )
//如果书籍名字中包含有EJB字样,则得到所有人
MEMBER OF操作符
可以使用MEMBER OF操作符检查集合-值路径表达式中是否存在标识符变量、单一值表达式或是输入参数。
select p from Person p where :interest member of p.interest
//查询兴趣(是集合)是中包含所输入的兴趣实体实例(:interest)的所有人
使用字符串函数
JPQL定义了内置函数方便使用。这些函数的使用方法和SQL中相应的函数方法类似。包括:
1. CONCAT 字符串拼接
2. SUBSTRING 字符串截取
3. TRIM 去掉空格
4. LOWER 转换成小写
5. UPPER 装换成大写
6. LENGTH 字符串长度
7. LOCATE 字符串定位
使用算术函数
JPQL仅仅支持了最低限度的算术函数集合,可以在JPQL的where和having子句中使用算术函数。JPQL定义的算术函数包括:
ABS 绝对值
SQRT 平方根
MOD 取余数
SIZE 取集合的数量
使用时间函数
JPQL像大多数语言一样提供了获得当前日期、时间或是时间标记的函数。这些函数转换为数据库专有的SQL函数,从数据库检索当前日期、时间或时间标记。包含的时间函数如下:
CURRENT_DATE 返回当前日期
CURRENT_TIME 返回当前时间
CURRENT_TIMESTAMP 返回当前时间标记
联结(连接/关联)
JPQL仍然支持和SQL中类似的关联语法:
left out join/left join
left out join/left join等,都是允许符合条件的右边表达式中的Entities 为空(需要显式使用left join/left outer join 的情况会比较少。)
inner join
inner join 要求右边的表达式必须返回Entities。
left join fetch/inner join fetch
在默认的查询中,Entity中的集合属性默认不会被关联,集合属性默认是延迟加载( lazy-load )。那么,left fetch/left out fetch/inner join fetch提供了一种灵活的查询加载方式来提高查询的性能(集合属性被关联,同Entity同时加载而不是在需要时再加载,这样就转换为SQL语句时为一条SQL语句,而不是加载Entity时一条语句,加载集合属性时有N(等于Entity数量)条语句,避免了N+1问题,提高了查询性能)。
使用聚合函数
JPQL提供的聚合函数有AVG、COUNT、MAX、MIN、SUM。在AVG、MAX、MIN、SUM函数中只能使用持久化字段,而在COUNT中可以使用任何类型的路径表达式或标识符。
COUNT返回的类型是Long,AVG是Double,SUM可能返回Long或Double。
分组
如果聚合函数不是select...from的唯一一个返回列,需要使用"GROUP BY"语句。"GROUP BY"应该包含select 语句中除了聚合函数外的所有属性。如果还需要加上查询条件,需要使用"HAVING"条件语句而不是"WHERE"语句
select p.grade, count(p) from Person p where p.age > 20 group by p.grade having count(*)>120
//返回年龄大于20的各年级的总人数(人数大于120)
排序
在JPQL中像SQL一样使用order by 来进行排序。"ASC"和"DESC"分别为升序和降序,JPQL中默认为ASC升序。
批删除和批更新
JPQL支持批量删除和批量更新的操作。和查询相同的是也是要使用EntityManager.createQuery方法来创建一个Query实例,不同的是在这里要使用Query.executeUpdate方法来直行删除和更新,该方法返回的值是操作的记录数。
命名查询
可以在实体bean上通过@NamedQuery or @NamedQueries预先定义一个或多个查询语句,减少每次因书写错误而引起的BUG。通常把经常使用的查询语句定义成命名查询。
定义单个命名查询:
@NamedQuery(name="getPerson", query= "select p from Person p where p.id=?1")
@Entity
public class Person implements Serializable{
如果要定义多个命名查询,应在@javax.persistence.NamedQueries里定义@NamedQuery:
@NamedQueries({
@NamedQuery(name="getPerson", query= "select p from Person p where p.id=?1"),
@NamedQuery(name="getPersonList", query= "select p from Person as p where p.age>?1")
})
@Entity
public class Person implements Serializable{
当命名查询定义好了之后,我们就可以通过名称执行其查询。代码如下:
Query query = em.createNamedQuery("getPerson");
query.setParameter(1, 1);
四、注意
1. JPQL语句的大小写敏感性:除了Java 类和属性名称外,查询都是大小写不敏感的。
2. 使用参数查询的时候除了基本类型的参数还可以使用实体参数。
相关文章推荐
- spring 3.0 + jpa + hibernate 的实现步骤,附完整工程包
- spring 3.0 + jpa + hibernate 的实现步骤,附完整工程包
- Spring3.0 JPA(hibernate3.6实现)整合问题之:java.lang.NoSuchMethodError: javax.persistence.spi.PersistenceUni
- 基于james3.0 的邮件系统(struts2.3.2 +spring3.0.1+jpa(hibernate3.6.5)实现)b/s模式--java邮件系统
- 基于james3.0 的邮件系统(struts2.3.2 +spring3.0.1+jpa(hibernate3.6.5)实现)b/s模式--java邮件系统 .
- 基于james3.0 的邮件系统(struts2.3.2 +spring3.0.1+jpa(hibernate3.6.5)实现)b/s模式 .
- 基于james3.0 的邮件系统(struts2.3.2 +spring3.0.1+jpa(hibernate3.6.5)实现)b/s模式 .
- 基于james3.0 的邮件系统(struts2.3.2 +spring3.0.1+jpa(hibernate3.6.5)实现)b/s模式--java邮件系统
- 基于james3.0 的邮件系统(struts2.3.2 +spring3.0.1+jpa(hibernate3.6.5)实现)b/s模式--java邮件系统
- 基于james3.0 的邮件系统(struts2.3.2 +spring3.0.1+jpa(hibernate3.6.5)实现)b/s模式
- 基于james3.0 的邮件系统(struts2.3.2 +spring3.0.1+jpa(hibernate3.6.5)实现)b/s模式--java邮件系统
- 基于james3.0 的邮件系统(struts2.3.2 +spring3.0.1+jpa(hibernate3.6.5)实现)b/s模式--java邮件系统
- [置顶] Spring整合Hibernate实现Spring Data JPA
- Spring2.5+Struts1.3.8+JPA(Hibernate实现)整合之四
- 【java】spring-data-jpa 集成hibernate实现多条件分页查询
- Spring2.5+Struts1.3.8+Jpa(Hibernate实现)整合之八
- spring data Jpa hibernate实现
- spring boot+spring data jpa(hibernate)完整项目
- Spring2.5+Struts1.3.8+JPA(Hibernate实现)整合之五
- 从最基本的Java工程搭建SpringMVC+SpringDataJPA+Hibernate