持久化组件
业务需求
业务应用在快速搭建过程中大多数都需要持久化,需要提供一种简单的持久化集成方式,快速搭建支持基础CRUD的环境,同时兼顾后期的扩展。持久化要简单易用,支持分页查询、保存和更新操作等。为了和其他iuap组件保持对Spring等第三方组件一致的版本依赖,需要平台提供基本的持久化、连接池等集成规范和示例。
解决方案
iuap持久化组件提供对通用持久化功能的集成,包括Spring Data JPA、Mybatis、Spring JDBC等,同时集成数据库连接池Apache Tomcat-jdbc。本组件没有对代码进行封装,作用在于集成对基础的Spring Data JPA方式和Mybatis方式的依赖,保证iuap其他组件和持久化组件所依赖的三方组件版本统一。
工程搭建时,可以引入iuap持久化组件,快速的对持久化层进行集成,使用示例工程中提供的Spring配置文件模板,对工程的持久化方案进行筛选。
功能说明
- 集成Spring Data JPA;
- 集成Mybatis;
- 集成Spring JDBC;
- 集成Tomcat JDBC数据库连接池;
- 支持JPA分页查询;
整体设计
依赖环境
- spring 4.0.5.RELEASE
- spring-data-jpa 1.6.0.RELEASE
- mybatis 3.3.0
- mybatis-spring 1.2.3
- hibernate-entitymanager 4.3.5.Final
- tomcat-jdbc 7.0.53
上述组件已经在iuap-persistence中依赖,不需要业务开发再次依赖,如果在集成前已经依赖,请自行处理版本冲突
注意事项
- 默认使用Tomcat jdbc的连接池,为后续的动态数据源做基础
- 集成Mybatis的基础依赖,如果需要单独使用Mybatis,请参考iuap-mybatis组件
- 事务控制上依赖spring的事务管理,使用Spring-data-jpa时,请注意需要使用JpaTransactionManager
- 业务上如果对其中的持久化方式可以明确,需要删减示例中不需要的配置项
使用说明
配置方式
1.maven工程配置对持久化组件的依赖
<dependency>
<groupId>com.yonyou.iuap</groupId>
<artifactId>iuap-persistence</artifactId>
<version>${iuap.modules.version}</version>
</dependency>
${iuap.modules.version} 为在pom.xml中定义的需要引入组件的version。
2.spring配置文件中,增加对连接池、数据源的依赖
<!-- 数据源配置, 使用Tomcat JDBC连接池 -->
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" 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="${jdbc.pool.maxActive}"/>
<property name="maxIdle" value="${jdbc.pool.maxIdle}"/>
<property name="minIdle" value="0"/>
<property name="maxWait" value="${jdbc.pool.maxWait}"/>
<property name="defaultAutoCommit" value="true"/>
<property name="minEvictableIdleTimeMillis" value="${jdbc.pool.minEvictableIdleTimeMillis}"/>
<property name="removeAbandoned" value="${jdbc.pool.removeAbandoned}"/>
<property name="removeAbandonedTimeout" value="${jdbc.pool.removeAbandonedTimeout}"/>
</bean>
3.如果使用Spring Data JPA方式进行持久化,spring配置文件中,增加如下配置
<!-- Jpa Entity Manager 配置 -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/>
<property name="packagesToScan" value="com.yonyou.iuap.entity"/>
<property name="jpaProperties">
<props>
<!-- 命名规则 My_NAME->MyName -->
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform">
<bean factory-method="getDialect" class="org.springside.modules.persistence.Hibernates">
<constructor-arg ref="dataSource"/>
</bean>
</property>
</bean>
<!-- Jpa 事务配置 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- Spring Data Jpa配置 -->
<jpa:repositories base-package="com.yonyou.iuap.repository"
transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory"/>
4.如果项目中使用Mybatis方式进行持久化,增加Mybatis的配置
<!-- MyBatis配置 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
<property name="typeAliasesPackage" value="com.yonyou.iuap.entity"/>
<!-- 显式指定Mapper文件位置 -->
<property name="mapperLocations" value="classpath:/mybatis/**/*Mapper.xml"/>
<property name="plugins">
<array>
<bean id="paginationInterceptor"
class="com.yonyou.iuap.persistence.mybatis.plugins.PaginationInterceptor">
<property name="properties">
<props>
<prop key="dbms">mysql</prop>
<prop key="sqlRegex">.*retrievePage.*</prop>
</props>
</property>
</bean>
</array>
</property>
</bean>
<!-- 扫描basePackage下所有以@MyBatisRepository标识的 接口-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.yonyou.iuap.repository"/>
<property name="annotationClass" value="com.yonyou.iuap.persistence.mybatis.anotation.MyBatisRepository"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
5.增加对事务的控制
<!-- 使用annotation定义事务 -->
<aop:aspectj-autoproxy/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 注意!!!单独使用mybatis时候事务配置可以采用DataSourceTransactionManager,如果和jpa一起使用,配置JpaTransactionManager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
6.JPA方式接口示例
public interface DemoEntityJpaDao extends PagingAndSortingRepository<DemoEntity, String> , JpaSpecificationExecutor<DemoEntity>
7.Mybatis方式接口示例
@MyBatisRepository
public interface IpuQuotationMapper extends PageMapper<IpuQuotation> {
int deleteByPrimaryKey(String id);
int insert(IpuQuotation record);
IpuQuotation selectByPrimaryKey(String id);
IpuQuotation selectChildrenByPrimaryKey(String id);
int updateByPrimaryKeySelective(IpuQuotation record);
int updateByPrimaryKey(IpuQuotation record);
PageResult<IpuQuotation> retrievePage(PageRequest pageRequest, @Param("subject") String subject);
}
8.对应service的业务方法上,增加事务注解
@Transactional
public int complexQuotationOperate(IpuQuotation quotation){
//事务的传播机制根据用户的需求来配置,如新启子事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
9:更多API操作和配置方式,请参考对应的示例工程(DevTool/examples/example_iuap_persistence)
Sping Data JPA代码示例
导入已有的Maven工程iuap_persistence_example,里面有简单的示例:
实体类示例
@Entity @Table(name="example_demo") @NamedQuery(name="DemoEntity.findAll", query="SELECT d FROM DemoEntity d") public class DemoEntity implements Serializable { private static final long serialVersionUID = 1L; @Id @Column(name="id") private String id; @NotBlank(message="测试编码不能为空!") private String code; private String memo; private String name; private String isdefault; public DemoEntity() { } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getCode() { return this.code; } public void setCode(String code) { this.code = code; } public String getMemo() { return this.memo; } public void setMemo(String memo) { this.memo = memo; } public String getName() { return this.name; } public String getIsdefault() { return isdefault; } public void setIsdefault(String isdefault) { this.isdefault = isdefault; } public void setName(String name) { this.name = name; } }
数据库操作类示例(只需要定义接口)
public interface DemoEntityJpaDao extends PagingAndSortingRepository<DemoEntity, String> , JpaSpecificationExecutor<DemoEntity>{ DemoEntity findByCode(String code); @Query("select d from DemoEntity d where code in (:codes)") List<DemoEntity> findByConditions(String[] codes); @Query(value = "select * from example_demo where id = ?1", nativeQuery=true) DemoEntity getDemoByNativeSql(String id); @Modifying @Query(value = "delete from example_demo where id = ?1", nativeQuery=true) void deleteDemoByIdWithNativeSql(String id); }
服务类示例
@Service public class DemoService { @Autowired private DemoEntityJpaDao dao; @Autowired private JdbcTemplate jt; public DemoEntity getDemoById(String id) { return dao.findOne(id); } public DemoEntity getDemoBySql(String id) { return dao.getDemoByNativeSql(id); } @Transactional public void deleteById(String id) { dao.delete(id); } @Transactional public void deleteDemoByIdUseSql(String id) { dao.deleteDemoByIdWithNativeSql(id); } public DemoEntity saveEntity(DemoEntity entity) throws SQLException { if (StringUtils.isBlank(entity.getId())) { entity.setId(UUID.randomUUID().toString()); } entity = dao.save(entity); return entity; } public Page<DemoEntity> getDemoPage(Map<String, Object> searchParams, PageRequest pageRequest) { Specification<DemoEntity> spec = buildSpecification(searchParams); return dao.findAll(spec, pageRequest); } /** * 创建动态查询条件组合. */ public Specification<DemoEntity> buildSpecification(Map<String, Object> searchParams) { Map<String, SearchFilter> filters = SearchFilter.parse(searchParams); Specification<DemoEntity> spec = DynamicSpecifications.bySearchFilter(filters.values(), DemoEntity.class); return spec; } }
Mybatis代码示例
实体类示例
public class IpuQuotation implements Serializable {
private static final long serialVersionUID = -9169987729255268660L; private String ipuquotaionid; private Date buyoffertime; private String contact; private Date createtime; private String description; private String phone; private Date processtime; private String processor; private Date qtexpiredate; private String subject; private Date ts; private Short dr; private List<IpuQuotationDetail> datailentitylist; public String getIpuquotaionid() { return ipuquotaionid; } public void setIpuquotaionid(String ipuquotaionid) { this.ipuquotaionid = ipuquotaionid == null ? null : ipuquotaionid.trim(); } public Date getBuyoffertime() { return buyoffertime; } public void setBuyoffertime(Date buyoffertime) { this.buyoffertime = buyoffertime; } ... ... public List<IpuQuotationDetail> getDatailentitylist() { return datailentitylist; } public void setDatailentitylist(List<IpuQuotationDetail> datailentitylist) { this.datailentitylist = datailentitylist; }
}
数据库操作类示例
@MyBatisRepository public interface IpuQuotationMapper extends PageMapper<IpuQuotation> { int deleteByPrimaryKey(String id); int insert(IpuQuotation record); IpuQuotation selectByPrimaryKey(String id); IpuQuotation selectChildrenByPrimaryKey(String id); int updateByPrimaryKeySelective(IpuQuotation record); int updateByPrimaryKey(IpuQuotation record); PageResult<IpuQuotation> retrievePage(PageRequest pageRequest, @Param("subject") String subject); }
映射文件示例(注意spring配置文件中对mybatis映射文件的位置)
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.yonyou.iuap.repository.quotation.IpuQuotationMapper"> <resultMap id="BaseResultMap" type="com.yonyou.iuap.entity.quotation.IpuQuotation"> <id column="ipuquotaionid" property="ipuquotaionid" jdbcType="CHAR"/> <result column="buyoffertime" property="buyoffertime" jdbcType="TIMESTAMP"/> <result column="contact" property="contact" jdbcType="VARCHAR"/> <result column="createtime" property="createtime" jdbcType="TIMESTAMP"/> <result column="description" property="description" jdbcType="VARCHAR"/> <result column="phone" property="phone" jdbcType="VARCHAR"/> <result column="processtime" property="processtime" jdbcType="TIMESTAMP"/> <result column="processor" property="processor" jdbcType="VARCHAR"/> <result column="qtexpiredate" property="qtexpiredate" jdbcType="TIMESTAMP"/> <result column="subject" property="subject" jdbcType="VARCHAR"/> <result column="ts" property="ts" jdbcType="TIMESTAMP"/> <result column="dr" property="dr" jdbcType="SMALLINT"/> </resultMap> <resultMap id="childrenMap" type="com.yonyou.iuap.entity.quotation.IpuQuotation"> <id column="ipuquotaionid" property="ipuquotaionid" jdbcType="CHAR"/> <result column="buyoffertime" property="buyoffertime" jdbcType="TIMESTAMP"/> <result column="contact" property="contact" jdbcType="VARCHAR"/> <result column="createtime" property="createtime" jdbcType="TIMESTAMP"/> <result column="description" property="description" jdbcType="VARCHAR"/> <result column="phone" property="phone" jdbcType="VARCHAR"/> <result column="processtime" property="processtime" jdbcType="TIMESTAMP"/> <result column="processor" property="processor" jdbcType="VARCHAR"/> <result column="qtexpiredate" property="qtexpiredate" jdbcType="TIMESTAMP"/> <result column="subject" property="subject" jdbcType="VARCHAR"/> <result column="ts" property="ts" jdbcType="TIMESTAMP"/> <result column="dr" property="dr" jdbcType="SMALLINT"/> <collection property="datailentitylist" ofType="com.yonyou.iuap.entity.quotation.IpuQuotationDetail"> <id column="c_id" property="ipuquotationdetailid" jdbcType="CHAR"/> <result column="c_productdesc" property="productdesc" jdbcType="VARCHAR"/> <result column="c_productname" property="productname" jdbcType="VARCHAR"/> <result column="c_purchaseamount" property="purchaseamount" jdbcType="NUMERIC"/> <result column="c_unit" property="unit" jdbcType="VARCHAR"/> <result column="c_ipuquotaionid" property="ipuquotaionid" jdbcType="CHAR"/> <result column="c_ts" property="ts" jdbcType="TIMESTAMP"/> <result column="c_dr" property="dr" jdbcType="SMALLINT"/> </collection> </resultMap> <sql id="Base_Column_List"> ipuquotaionid, buyoffertime, contact, createtime, description, phone, processtime, processor, qtexpiredate, subject, ts, dr </sql> <sql id="oneToManyColumn"> t.ipuquotaionid, t.buyoffertime, t.contact, t.createtime, t.description, t.phone, t.processtime, t.processor, t.qtexpiredate, t.subject, t.ts, t.dr, tc.ipuquotationdetailid as c_id, tc.productdesc as c_productdesc,tc.productname as c_productname, tc.purchaseamount as c_purchaseamount, tc.unit as c_unit, tc.ipuquotaionid as c_ipuquotaionid, tc.ts as c_ts,tc.dr as c_dr </sql> <select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from ipuquotation where ipuquotaionid = #{ipuquotaionid} </select> <select id="selectChildrenByPrimaryKey" parameterType="java.lang.String" resultMap="childrenMap"> select <include refid="oneToManyColumn"/> from ipuquotation t LEFT OUTER JOIN ipuquotationdetail tc on t.ipuquotaionid=tc.ipuquotaionid where t.ipuquotaionid = #{ipuquotaionid} </select> <select id="count" parameterType="map" resultType="int"> select count(*) from ipuquotation where 1=1 <if test="subject != null and subject!=''"> and subject like concat('%',#{subject}) </if> </select> <select id="queryPage" parameterType="map" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from ipuquotation where 1=1 <if test="subject != null and subject!=''"> and subject like concat('%',#{subject}) </if> <if test="page.sort!=null"> order by <foreach collection="page.sort" item="item" separator=" "> ${item.property} ${item.direction} </foreach> </if> limit #{page.size} offset #{page.offset} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.String"> delete from ipuquotation where ipuquotaionid = #{ipuquotaionid} </delete> <insert id="insert" parameterType="com.yonyou.iuap.entity.quotation.IpuQuotation"> insert into ipuquotation (ipuquotaionid, buyoffertime, contact, createtime, description, phone, processtime, processor, qtexpiredate, subject, ts, dr) values (#{ipuquotaionid,jdbcType=CHAR}, #{buyoffertime,jdbcType=TIMESTAMP}, #{contact,jdbcType=VARCHAR}, #{createtime,jdbcType=TIMESTAMP}, #{description,jdbcType=VARCHAR}, #{phone,jdbcType=VARCHAR}, #{processtime,jdbcType=TIMESTAMP}, #{processor,jdbcType=VARCHAR}, #{qtexpiredate,jdbcType=TIMESTAMP}, #{subject,jdbcType=VARCHAR}, #{ts,jdbcType=TIMESTAMP}, #{dr,jdbcType=SMALLINT}) </insert> <update id="updateByPrimaryKeySelective" parameterType="com.yonyou.iuap.entity.quotation.IpuQuotation"> update ipuquotation <set> <if test="buyoffertime != null"> buyoffertime = #{buyoffertime,jdbcType=TIMESTAMP}, </if> <if test="contact != null"> contact = #{contact,jdbcType=VARCHAR}, </if> <if test="createtime != null"> createtime = #{createtime,jdbcType=TIMESTAMP}, </if> <if test="description != null"> description = #{description,jdbcType=VARCHAR}, </if> <if test="phone != null"> phone = #{phone,jdbcType=VARCHAR}, </if> <if test="processtime != null"> processtime = #{processtime,jdbcType=TIMESTAMP}, </if> <if test="processor != null"> processor = #{processor,jdbcType=VARCHAR}, </if> <if test="qtexpiredate != null"> qtexpiredate = #{qtexpiredate,jdbcType=TIMESTAMP}, </if> <if test="subject != null"> subject = #{subject,jdbcType=VARCHAR}, </if> <if test="ts != null"> ts = #{ts,jdbcType=TIMESTAMP}, </if> <if test="dr != null"> dr = #{dr,jdbcType=SMALLINT}, </if> </set> where ipuquotaionid = #{ipuquotaionid,jdbcType=CHAR} </update> <update id="updateByPrimaryKey" parameterType="com.yonyou.iuap.entity.quotation.IpuQuotation"> update ipuquotation set buyoffertime = #{buyoffertime,jdbcType=TIMESTAMP}, contact = #{contact,jdbcType=VARCHAR}, createtime = #{createtime,jdbcType=TIMESTAMP}, description = #{description,jdbcType=VARCHAR}, phone = #{phone,jdbcType=VARCHAR}, processtime = #{processtime,jdbcType=TIMESTAMP}, processor = #{processor,jdbcType=VARCHAR}, qtexpiredate = #{qtexpiredate,jdbcType=TIMESTAMP}, subject = #{subject,jdbcType=VARCHAR}, ts = #{ts,jdbcType=TIMESTAMP}, dr = #{dr,jdbcType=SMALLINT} where ipuquotaionid = #{ipuquotaionid,jdbcType=CHAR} </update> <select id="retrievePage" parameterType="map" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from ipuquotation where 1=1 <if test="subject != null and subject!=''"> and subject like concat(#{subject},'%') </if> </select> </mapper>