项目中,采用的是最新版的框架。截至目前,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.还要再通过接口的形式,会比较繁琐。希望能找到更好的解决方案。