项目中,采用的是最新版的框架。截至目前,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.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.0.xsdhttp://www.springframework.org/schema/txhttp://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-placeholderignore-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;/* 注入实体管理器*/@PersistenceContextprivate EntityManager em;/*** 获取实体管理器,便于子类访问* 不可被子类重写* @return*/protected final EntityManager getEM() {return this.em;}/*** 设置类型,不可被子类重写* @param clazzToSet*/protected final void setClazz(Class<T> clazzToSet) {this.clazz = Preconditions.checkNotNull(clazzToSet);}@Overridepublic void save(T entity) {Preconditions.checkNotNull(entity);getEM().persist(entity);}@Overridepublic void update(T entity) {getEM().merge(entity);}@Overridepublic void delete(T entity) {getEM().remove(entity);}@Overridepublic 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;}@Overridepublic QueryResult<T> getScrollData(Class<T> entityclass, int pageNo,int pageSize, LinkedHashMap<String, String> orderby) {return getScrollData(entityclass, pageNo, pageSize, null, null,orderby);}@Overridepublic QueryResult<T> getScrollData(Class<T> entityclass, int pageNo,int pageSize, String whereJpql, Object[] params) {return getScrollData(entityclass, pageNo, pageSize, whereJpql,params, null);}@Overridepublic QueryResult<T> getScrollData(Class<T> entityclass, int pageNo,int pageSize) {return getScrollData(entityclass, pageNo, pageSize, null);}@Overridepublic 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> implementsIOperations<T> {/*** 抽象方法,用于被具体实现类访问,提供底层调用* * @return*/protected abstract IOperations<T> getDao();@Overridepublic void save(T entity) {getDao().save(entity);}@Overridepublic void update(T entity) {getDao().update(entity);}@Overridepublic void delete(T entity) {getDao().delete(entity);}@Overridepublic void delete(Object entityId) {getDao().delete(entityId);}@Overridepublic T load(Class<T> clazz, Object entityId) {return getDao().load(clazz, entityId);}@Overridepublic T load(Object entityId) {return getDao().load(entityId);}@Overridepublic T get(Class<T> clazz, Object entityId) {return getDao().get(clazz, entityId);}@Overridepublic T get(Object entityId) {return getDao().get(entityId);}@Overridepublic 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);}@Overridepublic QueryResult<T> getScrollData(Class<T> entityclass, int pageNo,int pageSize, LinkedHashMap<String, String> orderby) {return getDao().getScrollData(entityclass, pageNo, pageSize, orderby);}@Overridepublic QueryResult<T> getScrollData(Class<T> entityclass, int pageNo,int pageSize, String whereJpql, Object[] params) {return getDao().getScrollData(entityclass, pageNo, pageSize, whereJpql, params);}@Overridepublic QueryResult<T> getScrollData(Class<T> entityclass, int pageNo,int pageSize) {return getDao().getScrollData(entityclass, pageNo, pageSize);}@Overridepublic 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() {}@Overrideprotected 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 {@Testpublic 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.还要再通过接口的形式,会比较繁琐。希望能找到更好的解决方案。