项目中,采用的是最新版的框架。截至目前,Spring 版本为4.0.6 ,Hibernate 版本为4.3.5.1,在Hibernate 自带的包中,提供了JPA的最新版本,在集成的过程中,一直不顺利。
1.首先下载Spring和Hibernate的最新的库文件。这里提供集成好的下载链接。http://download.csdn.net/download/scherrer/7654035
2.导入项目中(导入Spring和Hibernate自带的相应的库文件,在实际执行过程中,会少一些依赖库,如aspectjrt.jar,aopalliance.jar等)
3.在http://commons.apache.org/ 找到DBCP2和commons-pool2 的库文件,并导入项目中。
4.配置applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<description>Spring公共配置 </description>
<!-- 使用annotation 自动注册bean, 并保证@Required、@Autowired的属性被注入 -->
<context:component-scan base-package="com.astar">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- JPA实体管理工厂的配置 -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
<property name="packagesToScan" value="com.astar.entity" /><!--待扫描的实体类包,不再需要persistence.xml了 -->
<property name="jpaProperties">
<props>
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!--指定实现JPA的适配器 -->
<bean id="hibernateJpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
</bean>
<!-- Jpa 事务配置 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true" />
<context:property-placeholder
ignore-unresolvable="true" location="classpath:config/mysql.properties" />
<!-- 数据源配置, 使用DBCP数据库连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<!-- Connection Info -->
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- Connection Pooling Info -->
<!-- <property name="maxActive" value="${dbcp.maxActive}" /> -->
<property name="maxIdle" value="${dbcp.maxIdle}" />
<property name="defaultAutoCommit" value="false" />
<!-- 连接Idle一个小时后超时 -->
<property name="timeBetweenEvictionRunsMillis" value="3600000" />
<property name="minEvictableIdleTimeMillis" value="3600000" />
</bean>
</beans>
这种配置方式,可不用再集成JPA的persistence.xml文件。
5. 开始环境测试,新建单元测试类
package com.astar.entity;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
/**
* 用于单元测试的Bean
*
* @author admin
*
*/
@SuppressWarnings("serial")
@Entity
@Table(name="t_unit_test_bean")
public class UnitTestBean implements Serializable {
@Id
//@GeneratedValue(generator = "system-uuid")
//@GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id;
@Column(length = 50)
private String name;
@Enumerated(EnumType.ORDINAL)
private String state;
@Lob
@Basic(fetch = FetchType.LAZY)
private String remark;
@Temporal(TemporalType.TIMESTAMP)
private Date time;
public UnitTestBean() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
6.在项目中的公共common包下,新建dao层。并编写IOperation泛型接口
package com.astar.common.dao;
import java.io.Serializable;
import java.util.LinkedHashMap;
import com.astar.common.base.QueryResult;
/**
* 数据访问层,全局接口
* 公共底层DAO操作接口,通过泛型实现泛类调用
* @author xiele
* @version 1.0
* @date 2014-07-19 10:11
*
*/
public interface IOperations <T extends Serializable> {
/**
* 保存(持久化)实体
* @param entity
*/
void save(final T entity);
/**
* 更新(合并)实体
* @param entity
*/
void update(final T entity);
/**
* 删除实体
* @param entity
*/
void delete(final T entity);
/**
* 通过id删除实体
* @param id
*/
void delete(final Object entityId);
/**
* 懒加载对象
* @param clazz
* @param entityId
* @return
*/
T load(Class<T> clazz, final Object entityId);
T load(final Object entityId);
/**
* 查询实体
* @param clazz
* @param entityId
* @return
*/
T get(Class<T> clazz, final Object entityId);
T get(final Object entityId);
/**
*
* @param entityclass
* 泛型
* @param pageNo
* 每页开始的索引
* @param pageSize
* 最大记录
* @param whereJpql
* 查询条件
* @param param
* 设置参数
* @param orderby
* 排序条件
* @return
*/
QueryResult<T> getScrollData(Class<T> entityclass,
int pageNo, int pageSize, String whereJpql, Object[] params,
LinkedHashMap<String, String> orderby);
/**
* 重载方法
*/
QueryResult<T> getScrollData(Class<T> entityclass,
int pageNo, int pageSize, LinkedHashMap<String, String> orderby);
/**
* 重载方法
*/
QueryResult<T> getScrollData(Class<T> entityclass,
int pageNo, int pageSize, String whereJpql, Object[] params);
/**
* 重载方法
*/
QueryResult<T> getScrollData(Class<T> entityclass,
int pageNo, int pageSize);
/**
* 重载方法
* 查询全部实体
*/
QueryResult<T> getScrollData(Class<T> entityclass);
}
再新建AbstractDaoSupport抽象类,实现该接口,目的是为了重用。
package com.astar.common.dao;
import java.io.Serializable;
import java.util.LinkedHashMap;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.astar.common.base.QueryResult;
import com.astar.common.util.SqlUtil;
import com.google.common.base.Preconditions;
/**
* 抽象DAO实现,便于被继承,用于处理通用的数据操作
* 默认:方法被加入事物,传播行为:Propagation.REQUIRED
* @author xiele
* @version 1.0
* @param <T>
*/
@Transactional
public abstract class AbstractDaoSupport<T extends Serializable> implements IOperations<T> {
/* Class类型,在执行过程中,动态设置类型 */
private Class<T> clazz;
/* 注入实体管理器*/
@PersistenceContext
private EntityManager em;
/**
* 获取实体管理器,便于子类访问
* 不可被子类重写
* @return
*/
protected final EntityManager getEM() {
return this.em;
}
/**
* 设置类型,不可被子类重写
* @param clazzToSet
*/
protected final void setClazz(Class<T> clazzToSet) {
this.clazz = Preconditions.checkNotNull(clazzToSet);
}
@Override
public void save(T entity) {
Preconditions.checkNotNull(entity);
getEM().persist(entity);
}
@Override
public void update(T entity) {
getEM().merge(entity);
}
@Override
public void delete(T entity) {
getEM().remove(entity);
}
@Override
public void delete(Object entityId) {
getEM().remove(getEM().getReference(clazz, entityId));
}
@Override
@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
public T load(Class<T> clazz, Object entityId) {
try {
return getEM().getReference(clazz, entityId);
} catch (Exception e) {
return null;
}
}
@Override
@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
public T load(Object entityId) {
try {
return getEM().getReference(this.clazz, entityId);
} catch (Exception e) {
return null;
}
}
@Override
@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
public T get(Class<T> clazz, Object entityId) {
return getEM().find(clazz, entityId);
}
@Override
@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
public T get(Object entityId) {
return getEM().find(this.clazz, entityId);
}
@SuppressWarnings("unchecked")
@Override
@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
public QueryResult<T> getScrollData(Class<T> entityclass, int pageNo,
int pageSize, String whereJpql, Object[] params,
LinkedHashMap<String, String> orderby) {
QueryResult<T> qr = new QueryResult<T>();
String entityName = SqlUtil.getEntityName(entityclass);
String sql = "select o from " + entityName + " o "
+ (whereJpql == null ? "" : "where " + whereJpql)
+ SqlUtil.buildOrderby(orderby);
String sql2 = "select count(o) from " + entityName + " o "
+ (whereJpql == null ? "" : "where " + whereJpql);
Query query = em.createQuery(sql2);
SqlUtil.setParamters(query, params);
qr.setCount((Long) query.getSingleResult());
if (qr.getCount() % pageSize == 0) {
qr.setPageCount(qr.getCount() / pageSize);
} else {
qr.setPageCount(qr.getCount() / pageSize + 1);
}
query = em.createQuery(sql);
SqlUtil.setParamters(query, params);
if (pageNo != -1 && pageSize != -1) {
if (pageNo > qr.getPageCount()) {
pageNo = qr.getPageCount().intValue();
}
if (pageNo < 1) {
pageNo = 1;
}
qr.setPageNo(pageNo);
query.setFirstResult((pageNo - 1) * pageSize);
query.setMaxResults(pageSize);
}
qr.setResults(query.getResultList());
return qr;
}
@Override
public QueryResult<T> getScrollData(Class<T> entityclass, int pageNo,
int pageSize, LinkedHashMap<String, String> orderby) {
return getScrollData(entityclass, pageNo, pageSize, null, null,
orderby);
}
@Override
public QueryResult<T> getScrollData(Class<T> entityclass, int pageNo,
int pageSize, String whereJpql, Object[] params) {
return getScrollData(entityclass, pageNo, pageSize, whereJpql,
params, null);
}
@Override
public QueryResult<T> getScrollData(Class<T> entityclass, int pageNo,
int pageSize) {
return getScrollData(entityclass, pageNo, pageSize, null);
}
@Override
public QueryResult<T> getScrollData(Class<T> entityclass) {
return getScrollData(entityclass, -1, -1, null, null, null);
}
}
7.在项目中的公用的common包中,新建service层,用于放置公用的抽象业务实现类。
package com.astar.common.service;
import java.io.Serializable;
import java.util.LinkedHashMap;
import javax.transaction.Transactional;
import com.astar.common.base.QueryResult;
import com.astar.common.dao.IOperations;
/**
* 抽象业务实现类
*
* @author xiele
* @version 1.0
*/
@Transactional
public abstract class AbstractService<T extends Serializable> implements
IOperations<T> {
/**
* 抽象方法,用于被具体实现类访问,提供底层调用
*
* @return
*/
protected abstract IOperations<T> getDao();
@Override
public void save(T entity) {
getDao().save(entity);
}
@Override
public void update(T entity) {
getDao().update(entity);
}
@Override
public void delete(T entity) {
getDao().delete(entity);
}
@Override
public void delete(Object entityId) {
getDao().delete(entityId);
}
@Override
public T load(Class<T> clazz, Object entityId) {
return getDao().load(clazz, entityId);
}
@Override
public T load(Object entityId) {
return getDao().load(entityId);
}
@Override
public T get(Class<T> clazz, Object entityId) {
return getDao().get(clazz, entityId);
}
@Override
public T get(Object entityId) {
return getDao().get(entityId);
}
@Override
public QueryResult<T> getScrollData(Class<T> entityclass, int pageNo,
int pageSize, String whereJpql, Object[] params,
LinkedHashMap<String, String> orderby) {
return getDao().getScrollData(entityclass, pageNo, pageSize, whereJpql, params, orderby);
}
@Override
public QueryResult<T> getScrollData(Class<T> entityclass, int pageNo,
int pageSize, LinkedHashMap<String, String> orderby) {
return getDao().getScrollData(entityclass, pageNo, pageSize, orderby);
}
@Override
public QueryResult<T> getScrollData(Class<T> entityclass, int pageNo,
int pageSize, String whereJpql, Object[] params) {
return getDao().getScrollData(entityclass, pageNo, pageSize, whereJpql, params);
}
@Override
public QueryResult<T> getScrollData(Class<T> entityclass, int pageNo,
int pageSize) {
return getDao().getScrollData(entityclass, pageNo, pageSize);
}
@Override
public QueryResult<T> getScrollData(Class<T> entityclass) {
return getDao().getScrollData(entityclass);
}
}
8.新建IUnitTestDao接口
package com.astar.unit;
import com.astar.common.dao.IOperations;
import com.astar.entity.UnitTestBean;
public interface IUnitTestDao extends IOperations<UnitTestBean>{
//让所有的DAO都实现基本的操作接口IOperations
//除了实现IOperations中的基本操作之外,特定的DAO要实现其他操作可以在对应的接口DAO中定义方法,
//此处UserDao的接口IUserDao不需要实现其他方法
}
9. 新建UnitTestDaoImpl实现类
package com.astar.unit;
import org.springframework.stereotype.Repository;
import com.astar.common.dao.AbstractDaoSupport;
import com.astar.entity.UnitTestBean;
@Repository("unitTestDao")
public class UnitTestDaoImpl extends AbstractDaoSupport<UnitTestBean> implements IUnitTestDao {
public UnitTestDaoImpl() {
setClazz(UnitTestBean.class);
}
}
10. 新建IUnitTestServcie接口
package com.astar.unit;
import com.astar.common.dao.IOperations;
import com.astar.entity.UnitTestBean;
public interface IUnitTestService extends IOperations<UnitTestBean>{
}
11 新建UnitTestServiceImpl实现类
package com.astar.unit;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.astar.common.dao.IOperations;
import com.astar.common.service.AbstractService;
import com.astar.entity.UnitTestBean;
@Service("unitTestService")
public class UnitTestServiceImpl extends AbstractService<UnitTestBean> implements IUnitTestService{
@Resource(name="unitTestDao")
private IUnitTestDao dao;
public UnitTestServiceImpl() {
}
@Override
protected IOperations<UnitTestBean> getDao() {
return this.dao;
}
}
12 新建单元测试类
package com.astar.unit;
import java.util.Date;
import java.util.UUID;
import javax.persistence.EntityManagerFactory;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.astar.entity.UnitTestBean;
public class SpringEnvTest {
@Test
public void test() {
ClassPathXmlApplicationContext cxt = new ClassPathXmlApplicationContext("config/applicationContext-dbcp.xml");
//EntityManagerFactory em = (EntityManagerFactory)cxt.getBean("entityManagerFacotry");
//System.out.println("EM: " + em);
IUnitTestDao id = (IUnitTestDao)cxt.getBean("unitTestDao");
System.out.println("ID: " + id);
IUnitTestService is = (IUnitTestService) cxt.getBean("unitTestService");
System.out.println("IS: " + is);
UnitTestBean utb = new UnitTestBean();
String uuid = UUID.randomUUID().toString();
System.out.println("UUID: " + uuid);
utb.setId(uuid);
utb.setName("unit");
utb.setTime(new Date());
utb.setRemark("关键字,去死吧");
is.save(utb);
}
}
13.结论
正常情况下,测试OK。要注意的是,实体的属性不要和数据库关键字冲突,比如desc,user,distinct等。这是我搭建的项目框架,便于小组的人分工开发,包层次结构参考上一篇文章,思想是Package By Feature。欢迎讨论。
14. 缺陷是,在中小系统中,每次都通过Service调用dao.还要再通过接口的形式,会比较繁琐。希望能找到更好的解决方案。