购物项目实战学习记录(4)——DAO设计(MyBatis)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012525096/article/details/86556307

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)查询函数分为selectOneselectListselectMap,根据需求进行查询,本项目没有使用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>

注意:parameterTypejava.util.List(JAVA类型),collectionlist

更新(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+实体类中定义的常量

看完之后,当做大概复习MyBatis的知识点(这个项目实战没涉及细节,不太好)。

猜你喜欢

转载自blog.csdn.net/u012525096/article/details/86556307