DAO设计
采用MyBatis进行与数据库的交互操作,使用Spring统一管理。
BaseMybatisDAO(Mybatis基类)
SqlSessionTemplate
MyBatis提供的支持Spring的模板类。
@Autowired private SqlSessionTemplate template;
对应的配置文件applicationContext.xml
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${driverClass}" />
<property name="url" value="${jdbcUrl}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
<property name="initialSize" value="10" />
<property name="maxActive" value="100" />
<property name="maxIdle" value="50" />
<property name="maxWait" value="5000" />
<property name="poolPreparedStatements" value="false" />
<property name="defaultAutoCommit" value="false" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath*:com/tortuousroad/**/mapper/*.xml" />
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory" />
</bean>
其中,(1)通过PropertyPlaceholderConfigurer
加载读取jdbc.properties
,配置了一个BasicDataSource
作为dataSource
。
(2)SqlSessionFactoryBean
用于初始化SqlSessionTemplate
。
(3)JAVA中通过这个配置进行SqlSessionTemplate
的自动注入。
查询
/**
* 查询指定SQL语句的所有记录
* @param sqlId SQL语句ID
* @return 查询到的结果集合
*/
public <T extends BaseEntity> List<T> findAll(String sqlId) {
return template.selectList(sqlId);
}
/**
* 查询指定SQL语句的所有记录
* @param sqlId SQL语句ID
* @param params 条件参数
* @return 查询到的结果集合
*/
public <T extends BaseEntity> List<T> findAll(String sqlId, Map<String, Object> params) {
return template.selectList(sqlId, params);
}
/**
* 查询指定SQL语句的所有记录
* @param sqlId SQL语句ID
* @param param 条件参数
* @return 查询到的结果集合
*/
public <T extends BaseEntity> List<T> findAll(String sqlId, Object param) {
return template.selectList(sqlId, param);
}
/**
* 查询指定SQL语句的一条记录
* @param sqlId SQL语句ID
* @return 查询到的实体
*/
public <T extends BaseEntity> T findOne(String sqlId) {
return template.selectOne(sqlId);
}
/**
* 根据条件查询指定SQL语句的一条记录
* @param sqlId SQL语句ID
* @param <T> 返回值类型
* @param params 条件参数
* @return 查询到的结果
*/
public <T extends BaseEntity> T findOne(String sqlId, Map<String, Object> params) {
return template.selectOne(sqlId, params);
}
public Long findId(String sqlId, Object param) {
return template.selectOne(sqlId, param);
}
/**
* 根据条件查询指定SQL语句的一条记录,主要用于关联查询的情况
* @param sqlId SQL语句ID
* @param param 条件参数
* @return 查询到的结果
*/
public <T extends BaseEntity> T findOne(String sqlId, Object param) {
return template.selectOne(sqlId, param);
}
public <T> T findOneObject(String sqlId, Map<String, Object> params) {
return template.selectOne(sqlId, params);
}
注:
(1)查询函数分为selectOne
、selectList
、selectMap
,根据需求进行查询,本项目没有使用selectMap
。
(2)返回值分为<T extends BaseEntity>
和Object
。
返回值为<T extends BaseEntity>
返回值是继承于BaseEntity的对象,则mapper.xml
中通常像如下定义:
<resultMap id="BaseResultMap" type="com.tortuousroad.admin.security.entity.AdminFunction">
<id column="id" property="id" jdbcType="INTEGER" />
<result column="name" property="name" jdbcType="VARCHAR" />
<result column="state" property="state" jdbcType="VARCHAR" />
<result column="url" property="url" jdbcType="VARCHAR" />
<result column="parent_id" property="parentId" jdbcType="INTEGER" />
<result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP" />
</resultMap>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer">
select
<include refid="Base_Column_List" />
from admin_function
where id = #{id,jdbcType=INTEGER}
</select>
定义了一个resultMap
,它与一个继承于BaseEntity
的类进行关联,返回这样一个对象。
返回值为Object
返回值是Object
,则mapper.xml
中通常像如下定义:
<select id="selectIdBySkuId" resultType="java.lang.Long"
parameterType="java.lang.Long">
select
id
from deal
where sku_id = #{skuId,jdbcType=BIGINT}
</select>
它通常查询一个字段,返回一个字段。
增加数据
单数据save
public <T extends BaseEntity> int save(String sqlId, T entity) {
return template.insert(sqlId, entity);
}
注意返回值类型,还是该实体类型,下面会说到为什么。
mapper.xml如下形式:
<insert id="insertSelective" parameterType="com.tortuousroad.admin.security.entity.AdminFunction">
<selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
insert into admin_function
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="name != null">
name,
</if>
<if test="state != null">
state,
</if>
<if test="url != null">
url,
</if>
<if test="parentId != null">
parent_id,
</if>
<if test="createTime != null">
create_time,
</if>
<if test="updateTime != null">
update_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=INTEGER},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="state != null">
#{state,jdbcType=VARCHAR},
</if>
<if test="url != null">
#{url,jdbcType=VARCHAR},
</if>
<if test="parentId != null">
#{parentId,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
#{createTime,jdbcType=TIMESTAMP},
</if>
<if test="updateTime != null">
#{updateTime,jdbcType=TIMESTAMP},
</if>
</trim>
</insert>
其中,parameterType
(参数)是继承于BaseEntity
的。
注意:ID是某种递增策略生成的,在插入时,并不知道ID的值,通过下面的形式获得,并赋值给keyProperty="id"
。插入后,返回值就是附带id参数
的这个实体类。
<selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
多数据saveBatch
public <T extends BaseEntity> int saveBatch(String sqlId, List<T> entities) {
return template.insert(sqlId, entities);
}
通常mapper.xml如下,使用循环进行insert
。
<insert id="batchInsertAdminUserRoles" parameterType="java.util.List">
insert into admin_role_function
(admin_func_id, admin_role_id, create_time, update_time)
values
<foreach collection="list" item="item" index="index" separator=",">
(
#{item.adminFunctionId,jdbcType=INTEGER},
#{item.adminRoleId,jdbcType=INTEGER},
#{item.createTime,jdbcType=TIMESTAMP},
#{item.updateTime,jdbcType=TIMESTAMP}
)
</foreach>
</insert>
注意:parameterType
为java.util.List
(JAVA类型),collection
为list
。
更新(Update)
/**
* 更新指定SQL的数据
* @param sqlId SQL语句ID
* @param entities 要更新的对象
* @return 成功更新的记录数
*/
public <T extends BaseEntity> int update(String sqlId, T... entities) {
if (null != entities && entities.length == 1) {
return template.update(sqlId, entities[0]);
}
return template.update(sqlId, Arrays.asList(entities));
}
/**
* 更新指定SQL的数据
* @param sqlId SQL语句ID
* @param params 参数
* @return 成功更新的记录数
*/
public int update(String sqlId, Map<String, Object> params) {
return template.update(sqlId, params);
}
注意:(1)第一个方法,参数为可变长度的参数(JDK规定只能在最后一个参数)。
(2)第一个方法中,要注意长度为1和长度为1以上的参数区分。因为涉及到mapper.xml中的设计,长度为1时直接取对应的字段名取值;而长度大于1时,需要针对List
进行处理。
删除
/**
* 删除指定条件的SQL的数据
* @param sqlId
* @param key
* @return
*/
public int delete(String sqlId, Object key) {
return template.delete(sqlId, key);
}
/**
* 删除指定SQL的数据
* @param sqlId SQL语句ID
* @param params 查询参数
* @return 成功删除记录数
*/
public int delete(String sqlId, Map<String, Object> params) {
return template.delete(sqlId, params);
}
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
delete from admin_role
where id = #{id,jdbcType=INTEGER}
</delete>
没啥可说的。
另一种设计:DAO作为工具类
public final class CommonMybatisDAO {
private static SqlSessionTemplate template = SpringApplicationContext.getBean(SqlSessionTemplate.class);
//使用场景
//业务service的方法调用CommonMybatisDAO的save方法保存实体进入数据库
private static <T extends BaseEntity> String getSqlId(Class<T> clazz) {
StackTraceElement element = Thread.currentThread().getStackTrace()[2];
return clazz.getName() + "." + element.getMethodName();
}
private static <T extends BaseEntity> String getMapperName(Class<T> clazz) {
return clazz.getName() + "Mapper.";
}
public static <T extends BaseEntity> void save(T entity) {
String sqlId = getSqlId(entity.getClass());
template.insert(sqlId, entity);
}
public static <T extends BaseEntity> void save(String sqlId, T entity) {
String finalSqlId = getMapperName(entity.getClass()) + sqlId;
template.insert(finalSqlId, entity);
}
public static <T extends BaseEntity> List<T> findAll(Class<T> clazz) {
String sqlId = getSqlId(clazz);
return template.selectList(sqlId);
}
public static <T extends BaseEntity> List<T> findAll(Class<T> clazz, String sqlId) {
return template.selectList(getMapperName(clazz) + sqlId);
}
//CRUD
}
思想
简化参数:只想传入实体类。
问题:如何解决sqlID对应关系的问题。
折中:
(1)Service不同方法中:实体类类名生成的Mapper+调用的方法名
(2)Service相同方法中:实体类类名生成的Mapper+实体类中定义的常量