概述

FDP快速开发平台,使用Srping+mybatis做为基础框架,并在它的基础上做了很好的扩展,功能强大并且简单易学。

DAO层是FDP的核心模块,具有以下特点:

  1. 功能非富:CrudDao基类提供了单表操作的14个方法,分页模块(Page分页、PaginationInterceptor分页插件),Wrapper是SQL语句where条件的组装工具可完成复杂的查询,关系映射、延迟加载、1+N。
  2. 开发效率高:通过代码生成工具,就可快速生成Dao、xml文件、实体类,开发人员几乎不需要写代码。
  3. 扩展性十分优秀:为开发人员写代码留有独立的扩展文件,灵活度很大很大,不受任何束缚。高效与灵活并存,是FDP快速开发平台优点。

UML类图

先通过DAO层的 类图 ,来了解一下DAO层的都有哪些类。

相关类介绍

接口 说明 创建人
BaseDao 备扩展 FDP平台自带
CrudDao 是所有业务xxxDao的父接口,CrudDao提供了对单表操作的14个通用方法,可含盖95%的对单表的业务操作,极大的简化CRUD的开发。 FDP平台自带
ProductSpuDao

商品业务专用DAO,继承了CrudDao的14个通用方法。是空文件,供开发人员写 扩展 方法。

开发人员使用代码生成工具创建
ProductSpuDaoBase.xml 14条商品表的SQL保存在ProductSpuDaoBase.xml文件中,本文件是由代码生成工具生成的。 开发人员使用代码生成工具创建
ProductSpuDao.xml 是空文件,请把你的 扩展 SQL写到 当前 文件中 开发人员使用代码生成工具创建
TreeDao 树结构专用Dao,提供了tree操作的常用方法。并 继承了CrudDao的14个通用方法。 FDP平台自带
ProductCategoryDao

商品分类业务专用DAO,继承了CrudDao的14个通用方法,继承了TreeDao的N个通用方法。 是空文件,供开发人员写 扩展 方法。

开发人员使用代码生成工具创建
ProductCategoryDaoBase.xml 14条商品分类表的SQL保存在本文件中,本文件是由代码生成工具生成的。 开发人员使用代码生成工具创建
ProductCategoryDaoTree.xml 树操作专用SQL 保存在本文件中,本文件是由代码生成工具生成的。 开发人员使用代码生成工具创建
ProductCategoryDao.xml 是空文件,请把你的 扩展 SQL写到 当前 文件中 开发人员使用代码生成工具创建

DAO接口与XML文件

一个DAO接口配两个xml文件,目的是代码生成工具生成的代码与开发人员手写的代码分开,不要互相覆盖,可反复多次生成。

ProductSpuDaoBase.xml文件是父文件,是由代码生成工具生成的,内含有单表操作的常用SQL。
ProductSpuDao.xml文件是子文件,内容为空,供你写扩展SQL。
ProductSpuDao.xml文件与ProductSpuDaoBase.xml文件,使用了相同的全名空间,在运行时会合并起来。
ProductSpuDaoBase.xml文件,因为不包含你写的SQL,可使用代码生成工具反复生成,如表加了字段时就再生成一遍。
第二次生成时,ProductSpuDao.xml文件的内容你要自行做好备份。

CrueDao基类的通用方法说明

CrudDao基类提供了单表操作的14个方法, 通用的14条sql , 具体如下表

分类 CrudDao提供的14个通用单表操作方法 代码生成工具生成的单表操作的14个SQL 有del_flag逻辑删除字段的处理逻辑
查询

public T selectById( @Param (value= "id" ) Long id );

selectById (根据主键查询记录)

按ID查,无del_flag条件

public List<T> selectByIdIn( @Param (value= "list" ) List<Object> list );

selectByIdIn (按多个主键查询) 按多个ID查,无del_flag条件
public List<T> selectAll( @Param (value= "w" ) Wrapper wrapper ); selectAll (查询所有记录集,无条件,可选择带distinct) 执行时有del_flag条件,是从实体类中获取的。实体类默认带del_flag='0',默认只查出“正常”状态的记录

public List<T> selectByWhere( @Param (value= "p" ) Page<T> page , @Param (value= "w" ) Wrapper wrapper );

selectByWhere (根据条件查询记录集,可选择带distinct) 执行时有del_flag条件,是从实体类中获取的。实体类默认带del_flag='0',默认只查出“正常”状态的记录

public int countByWhere( @Param (value= "w" ) Wrapper wrapper );

countByWhere (根据条件查询记录总数,可选择带distinct) 执行时有del_flag条件,是从实体类中获取的。实体类默认带del_flag='0',默认只查出“正常”状态的记录
插入

public int insert( @Param (value= "entity" ) T entity );

insert (插入) -

public int insertSelective( @Param (value= "entity" ) T entity );

insertSelective (插入,只把非空的值插入到对应的字段) -
更新

public int updateById( @Param (value= "entity" ) T entity );

updateById (根据主键更新记录) 只按ID修改,无del_flag条件

public int updateByIdSelective( @Param (value= "entity" ) T entity );

updateByIdSelective (根据主键更新记录,只把非空的值更到对应的字段) 只按ID 修改 ,无del_flag条件

public int updateByWhere( @Param (value= "entity" ) T entity , @Param (value= "w" ) Wrapper wrapper );

updateByWhere (根据条件更新记录) 执行时有del_flag条件,是从实体类中获取的。实体类默认带del_flag='0',默认只修改“正常”状态的记录

public int updateByWhereSelective( @Param (value= "entity" ) T entity , @Param (value= "w" ) Wrapper wrapper );

updateByWhereSelective (根据条件更新记录,只把非空的值更到对应的字段) 执行时有del_flag条件,是从实体类中获取的。实体类默认带del_flag='0',默认只修改“正常”状态的记录
删除

public int deleteById( @Param (value= "id" ) Long id );

deleteById (根据主键删除记录) 是update语句,修改del_flag='1' 实现逻辑删除

public int deleteByIdIn( @Param (value= "list" ) List<Object> list );

deleteByIdIn (根据多个主键批量删除记录) 是delete语句,按多个ID删除,无del_flag条件。 这是有问题的未实现逻辑删除。

public int deleteByWhere( @Param (value= "w" ) Wrapper wrapper );

deleteByWhere (根据条件删除记录) 是delete语句,执行时有del_flag条件,是从实体类中获取的。实体类默认带del_flag='0'默认只删除出“正常”状态的记录。 这是有问题的未实现逻辑删除。

上表中出现了几个关键的类,介绍一下:

  1. Page<T> page:分页专用类,实现分页功能。
  2. Wrapper wrapper: SQL语句的where条件组装工具类,可灵活的组装where条件,方便的写各种复杂查询的select语句。
  3. T entity:实体类。

DAO 层的安全设计

1、防止无where条件时,删除全表、更新全表

以下3条sql已做防范
deleteByWhere
updateByWhere
updateByWhereSelective

2、防止SQL注入

由于低层使用了mybatis, 使用#{}占位符来传参数,避免sql注入

Wrapper类使用的占位符{0}{1}来参数,避免sql注入。在Wrapper中会检查“拼接sql”的行为来强制你使用占位符。

对于order by xxx,group by  xxx,可能存在sql注入,有正则检查器来保驾护航。

如何返回自增长生成的主键

FDP的数据库表主建生成策略有两大类

1、由外部指定主键。

2、自增长的主建(mysql自增长、Oracle序列)。

若采购每2种自增长的主建,则要求Dao层在insert后,返回由数据库自增长生成的主键,供后面的业务使用。

些功能FDP都实现了,采用的方法是在Mybatis XML中 < insert id = "insert" keyProperty = "entity.brandId" keyColumn = "brand_id" useGeneratedKeys = "true" >,来返回数据库生成的主键。

Oracle序列的命名约定

若采用Oracle数据库:若表名叫product_brand,那么相应的序列名就必须叫product_brand_seq,代码生成工具生成的SQL中已使用product_brand_seq这个名称。

CrudDao基类的源代码

CrudDao类的源码
package com.sicheng.common.persistence;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.sicheng.common.persistence.wrapper.Wrapper;
/**
 * <p>标题: DAO支持类实现</p>
 * <p>描述: </p>
 * @param <T>
 */
public interface CrudDao<T> extends BaseDao {
	/**
	 * 获取单条数据
	 * @param id 主键
	 * @return
	 */
	public T selectById(@Param(value="id") Long id);
	
	/**
	 * 查询 select * from a where id in(...)
	 * @param list 主键集合
	 * @return
	 */
	public List<T> selectByIdIn(@Param(value="list") List<Object> list);
	
	/**
	 * 查询数据列表,如果需要分页,请设置分页对象,如:entity.setPage(new Page<T>());
	 * @param wrapper 查询条件,可为null。或new一个Wrapper对象,用于控制order by 排序、控制del_flag、控制distinct
	 * 					入参为null,或入参为new一个Wrapper对象但无属性值,会查全表
	 * @param page 分页,null表示不分页
	 * @return
	 */
	public List<T> selectByWhere(@Param(value="p") Page<T> page,@Param(value="w") Wrapper wrapper);
	
	/**
	 * 查询所有数据列表
	 * @param entity 可为null。或new一个Wrapper对象,用于控制order by 排序、控制del_flag、控制distinct
	 * @return
	 */
	public List<T> selectAll(@Param(value="w") Wrapper wrapper);
	
	/**
	 * 插入数据
	 * 如果要在entity中带回自增长生成的主键值,mybatis的xml中要添加<insert id="insertSelective" keyProperty="pageId" useGeneratedKeys="true">
	 * @param entity
	 * @return 受影响的行数
	 */
	public int insert(@Param(value="entity") T entity);
	
	/**
	 * 插入,只把非空的值插入到对应的字段
	 * 如果要在entity中带回自增长生成的主键值,mybatis的xml中要添加<insert id="insertSelective" keyProperty="pageId" useGeneratedKeys="true">
	 * @param entity
	 * @return 受影响的行数
	 */
	public int insertSelective(@Param(value="entity") T entity);
	
	/**
	 * 根据主键更新记录,更新除了主键的所有字段
	 * @param entity
	 * @return 受影响的行数
	 */
	public int updateById(@Param(value="entity") T entity);
	
	/**
	 * 根据条件更新记录,更新除了主键的所有字段
	 * @param entity 数据实体,用于存储数据,这些数据将被update到表中
	 * @param wrapper 条件,用于where条件,找出符合条件的数据。入参为null,或入参为new一个Wrapper对象但无属性值,执行sql时会报错,防止更新全表
	 * @return 受影响的行数
	 * 
	 * 注意:wrapper.entity.setDelFlag(null);把del_flag设为null,表示del_flag不做为条件查询,请你根据业务情况自己把握
	 */
	public int updateByWhere(@Param(value="entity") T entity,@Param(value="w") Wrapper wrapper);
	
	/**
	 * 根据主键更新记录,只把非空的值更到对应的字段
	 * @param entity 数据实体,用于存储数据,这些数据将被update到表中
	 * @return 受影响的行数
	 */
	public int updateByIdSelective(@Param(value="entity") T entity);
	
	/**
	 * 根据条件更新记录,只把非空的值更到对应的字段
	 * @param entity 数据实体,用于存储数据,这些数据将被update到表中
	 * @param wrapper 条件,用于where条件,找出符合条件的数据。入参为null,或入参为new一个Wrapper对象但无属性值,执行sql时会报错,防止更新全表
	 * @return 受影响的行数
	 * 
	 * 注意:wrapper.entity.setDelFlag(null);把del_flag设为null,表示del_flag不做为条件查询,请你根据业务情况自己把握
	 */
	public int updateByWhereSelective(@Param(value="entity") T entity,@Param(value="w") Wrapper wrapper);
	
	/**
	 * 删除数据
	 * (如果有del_flag字段,就逻辑删除,更新del_flag字段为1表示删除)
	 * (如果无del_flag字段,就物理删除)
	 * @param id 主键
	 * @return 受影响的行数
	 */
	public int deleteById(@Param(value="id") Long id);
	
	/**
	 * 根据主键批量删除记录 
	 * @param list 一批主键
	 * @return
	 */
	public int deleteByIdIn(@Param(value="list") List<Object> list);
	/**
	 * 删除数据(物理删除)
	 * @param wrapper 删除条件。入参为null,或入参为new一个Wrapper对象但无属性值,执行SQL会报错,防止删除全表
	 * @return 受影响的行数
	 * 
	 * 注意:data.setDelFlag(null);//把del_flag设为null,表示del_flag不做为条件查询,请你根据业务情况自己把握
	 */
	public int deleteByWhere(@Param(value="w") Wrapper wrapper);
	
	/**
	 * 根据条件查询记录总数
	 * @param wrapper 可为null。或new一个Wrapper对象,用于控制del_flag、控制distinct
	 * @return 总行数
	 * 
	 * 注意:data.setDelFlag(null);//把del_flag设为null,表示del_flag不做为条件查询,请你根据业务情况自己把握
	 */
	public int countByWhere(@Param(value="w") Wrapper wrapper);
	
	
}