• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

dexcoder-assistant: 已重构发布新版,请移步:https://gitee.com/selfly/sonsure-dum ...

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

dexcoder-assistant

开源软件地址:

https://gitee.com/selfly/dexcoder-assistant

开源软件介绍:

新版全新重构,更加方便易用,请移步:https://gitee.com/selfly/sonsure-dumper

Dexcoder快速开发辅助工具包

该通用dal是在开发过程中,对于简单封装的通用dao或数据访问层使用深感痛苦与不便,由此进行了整合和改进发展而来。

如果你不喜欢用HibernateMybaits这类ORM框架,喜欢Spring JdbcTemplateDbUtils,那么可以试试这个封装的通用dal,这可能是目前封装的最方便易用的通用dal层了。

最近更新:

版本 2.3.5 更新时间:2016-06-08

  • 重构分页功能,支持sql解析方式,用户可自由选择
  • 修正Criteria方式先使用queryCount再queryList,order by丢失问题
  • 修正RunBinder拦截器事务嵌套有错时,事务回滚出错问题

详细更新日志

配置动态数据源请看这里:在dexcoder-dal中使用动态数据源并设置读写分离

数据水平分表请看这里:在dexcoder-dal中实现分表数据水平拆分

##核心组件dexcoder-dal使用说明

dexcoder-dal的一些特性:

  1. 一个dao即可以搞定所有的实体类,不必再一个个建立跟实体对应的继承于类似BaseDao这类“通用dao”了。
  2. 各类方法参数除了Entity外,支持更强大的Criteria方式。
  3. sql的where条件支持一些复杂的条件,如=!=orinnot in甚至是执行函数。
  4. 允许在查询时指定使用哪个字段进行排序,可以指定多个进行组合升降序自由排序。
  5. 支持在查询时指定返回字段的白名单和黑名单,可以指定只返回某些字段或不返回某些字段。
  6. select查询时支持函数,count()max()to_char()、甚至是distinct,理论上都可以支持。
  7. 方便强大的分页功能,无须额外操作,二三行代码搞定分页,自动判断数据库,无须指定。
  8. 可以使用{}[]完成一些特殊的操作,{}中的代码将原生执行,[]中的代码会进行命名转换,一般fieldName转columnName。
  9. 支持执行自定义sql。
  10. 支持使用类似mybatis的方式执行自定义sql。
  11. 支持读写分离和动态数据源。
  12. 对于数据分表水平拆分支持友好。

命名上遵循了约定优于配置的原则,典型约定如下:

  • 表名约定 USER_INFO表实体类名为UserInfo
  • 字段名约定 USER_NAME实体类中属性名为userName
  • 主键名约定 USER_INFO表主键名为USER_INFO_ID,同理实体类中属性名为userInfoId
  • Oracle序列名约定 USER_INFO表对应的主键序列名为SEQ_USER_INFO

当然,这些你可以在扩展中改变它,但不建议这么做,这本身就是一个良好的规范。

要在项目中使用通用dao十分简单,目前已上maven中央库,直接在pom.xml中添加依赖:

<dependency>    <groupId>com.dexcoder</groupId>    <artifactId>dexcoder-dal-spring</artifactId>    <version>${version}</version></dependency>

然后在spring的配置文件中声明如下bean:

<bean id="jdbcDao" class="com.dexcoder.dal.spring.JdbcDaoImpl">    <property name="jdbcTemplate" ref="jdbcTemplate"/></bean><!--需要分页时声明--><bean id="pageControl" class="com.dexcoder.dal.spring.page.PageControl"></bean>

接下来就可以注入到您的Service或者其它类中使用了。

##下面是一些常用的方法示例,这里的Entity对象为User,对于任何的Entity都是一样的.

先来看一下User对象及它继承的Pageable

public class User extends Pageable {    private Long              userId;    private String            loginName;    private String            password;    private Integer           userAge;    private String            userType;    private String            email;    private Date              gmtCreate;	//......}

Pageable对象,用来保存页码、每页条数信息以支持分页

public class Pageable implements Serializable {	/** 每页显示条数 */	protected int             itemsPerPage     = 20;	/** 当前页码 */	protected int             curPage          = 1;	//......}

都是普通的JavaBean对象,下面来看看如何进行具体的增删改查,每种操作都演示了EntityCriteria两种方式。

insert操作

Entity方式

User user = new User();user.setLoginName("selfly_a");//......Long userId = jdbcDao.insert(user);

Criteria 方式

Criteria criteria = Criteria.insert(User.class).into("loginName", "selfly_b").into("password", "12345678")    .into("email", "[email protected]").into("userAge", 22).into("userType", "2").into("gmtCreate", new Date());Long userId = jdbcDao.insert(criteria);

save操作,和insert的区别在于不处理主键,由调用者指定

Entity方式

User user = new User();user.setUserId(-1L);//......jdbcDao.save(user);

Criteria 方式

Criteria criteria = Criteria.insert(User.class).into("userId", -2L).into("loginName", "selfly-2")    .into("password", "12345678").into("email", "[email protected]").into("userAge", 22).into("userType", "2")    .into("gmtCreate", new Date());jdbcDao.save(criteria);

update操作

Entity方式

User user = new User();user.setUserId(57L);user.setPassword("abcdef");//方式一 为null的属性值将被忽略jdbcDao.update(user);//方式二 为null的属性值将更新到数据库jdbcDao.update(user,false);

Criteria方式

//criteria方式这里的email设为null也将被更新Criteria criteria = Criteria.update(User.class).set("password", "update222").set("email",null)    .where("userId", new Object[] { 56L, -1L, -2L });jdbcDao.update(criteria);

###get操作

根据主键

User user = jdbcDao.get(User.class, 63L);

Criteria方式

//criteria,主要用来指定字段白名单、黑名单等Criteria criteria = Criteria.select(User.class).include("loginName");User user = jdbcDao.get(criteria, 73L);

###delete操作

根据主键

jdbcDao.delete(User.class, 57L);

Entity方式

//会把不为空的属性做为where条件User u = new User();u.setLoginName("selfly-1");u.setUserType("1");jdbcDao.delete(u);

Criteria方式

//where条件使用了orCriteria criteria = Criteria.delete(User.class).where("loginName", new Object[] { "liyd2" })    .or("userAge", new Object[]{64});jdbcDao.delete(criteria);

列表查询操作

所有结果

List<User> users = jdbcDao.queryList(User.class);

以Entity中不为空的属性作为查询条件

User user = new User();user.setUserType("1");//......List<User> users = jdbcDao.queryList(user);

Criteria方式,可以指定黑白名单、排序字段等

Criteria criteria = Criteria.select(User.class).exclude("userId")    .where("loginName", new Object[]{"liyd"}).asc("userAge").desc("userId");List<User> users = jdbcDao.queryList(criteria);

指定逻辑操作符

//使用了like,可以换成!=、in、not in等Criteria criteria = Criteria.select(User.class).where("loginName", "like",    new Object[] { "%liyd%" });user.setUserAge(16);//这里entity跟criteria方式混合使用了,建议少用List<User> users = jdbcDao.queryList(user, criteria.include("userId"));

count记录数查询,除了返回值不一样外,其它和列表查询一致

//Entity方式user.setUserName("liyd");int count = jdbcDao.queryCount(user);//Criteria方式Criteria criteria = Criteria.select(User.class).where("loginName", new Object[] { "liyd" })    .or("userAge", new Object[]{27});int count = jdbcDao.queryCount(criteria);

查询单个结果,参数使用方式同上

//EntityUser user = jdbcDao.querySingleResult(user);//CriteriaCriteria criteria = Criteria.select(User.class).where("loginName", new Object[] { "liyd" })    .and("userId", new Object[]{23L});User u = jdbcDao.querySingleResult(criteria);

指定属性白名单,在任何查询方法中都可以使用

//将只返回loginNameCriteria criteria = Criteria.select(User.class).include("loginName");User u = jdbcDao.get(criteria, 23L);

指定属性黑名单,在任何查询方法中都可以使用

//将不返回loginNameCriteria criteria = Criteria.select(User.class).exclude("loginName");User u = jdbcDao.get(criteria, 23L);

指定排序

//指定多个排序字段,asc、descCriteria criteria = Criteria.select(User.class).exclude("userId")    .where("loginName", new Object[]{"liyd"}).asc("userId").desc("userAge");List<User> users = jdbcDao.queryList(criteria);

分页

直接传入Entity,继承于Pageable

//进行分页,只需要增加这行,列表查询方式跟上面没有任何区别PageControl.performPage(user);//分页后该方法即返回null,由PageControl中获取jdbcDao.queryList(user);Pager pager = PageControl.getPager();//列表List<User> users = pager.getList(User.class);//总记录数int itemsTotal = pager.getItemsTotal();

直接传入页码和每页大小

//直接传入页码和每页条数PageControl.performPage(1, 10);//使用Criteria方式,并指定排序字段方式为ascCriteria criteria = Criteria.select(User.class).include("loginName", "userId")    .where("loginName", new Object[]{"liyd"}).asc("userId");jdbcDao.queryList(criteria);Pager pager = PageControl.getPager();

使用智能分页解析

默认使用的简单分页在数据量较大,在自动做count()查询时可能会出现效率问题.

这时可以选用sql解析的智能分页.

只需要在声明PageControl时注入SmartPageSqlHandler来替换默认的SimplePageSqlHandler即可:

<bean id="pageControl" class="com.dexcoder.dal.spring.page.PageControl">    <property name="pageSqlHandler" ref="smartPageSqlHandler"/></bean><bean id="smartPageSqlHandler" class="com.dexcoder.dal.spring.page.SmartPageSqlHandler"/>

同时别忘了,使用智能分页需要添加JSqlParser的依赖:

<dependency>    <groupId>com.github.jsqlparser</groupId>    <artifactId>jsqlparser</artifactId>    <version>${jsqlparser.version}</version></dependency>

不同的属性在括号内or的情况:

Criteria criteria = Criteria.select(User.class)    .where("userType", new Object[] { "1" }).begin()    .and("loginName", new Object[] { "selfly" })    .or("email", new Object[] { "[email protected]" }).end()    .and("password", new Object[] { "123456" });User user = jdbcDao.querySingleResult(criteria);

执行函数

//max()Criteria criteria = Criteria.select(User.class).addSelectFunc("max([userId])");Long userId = jdbcDao.queryForObject(criteria);//count()Criteria criteria = Criteria.select(User.class).addSelectFunc("count(*)");Long count = jdbcDao.queryForObject(criteria);//distinctCriteria criteria = Criteria.select(User.class).addSelectFunc("distinct [loginName]");List<Map<String, Object>> mapList = jdbcDao.queryForList(criteria);

默认情况下,addSelectFunc方法返回结果和表字段互斥,并且没有排序,如果需要和表其它字段一起返回并使用排序,可以使用如下代码:

Criteria criteria = Criteria.select(User.class).addSelectFunc("DATE_FORMAT(gmt_create,'%Y-%m-%d %h:%i:%s') date",false,true);List<Map<String, Object>> mapList = jdbcDao.queryForList(criteria);

这是在select中执行函数,那怎么在update和where条件中执行函数呢?前面提到的{}[]就可以起到作用了。

看下面代码:

Criteria criteria = Criteria.update(User.class).set("[userAge]", "[userAge]+1")    .where("userId", new Object[] { 56L });jdbcDao.update(criteria);

以上代码将执行sql:UPDATE USER SET USER_AGE = USER_AGE+1 WHERE USER_ID = ?[]中的fieldName被转换成了columnName,

也可以使用{}直接写columnName,因为在{}中的内容都是不做任何操作原生执行的,下面代码效果是一样的:

Criteria criteria = Criteria.update(User.class).set("{USER_AGE}", "{USER_AGE + 1}")    .where("userId", new Object[] { 56L });jdbcDao.update(criteria);

同理,在where中也可以使用该方式来执行函数:

Criteria criteria = Criteria.select(User.class).where("[gmtCreate]", ">",    new Object[] { "str_to_date('2015-10-1','%Y-%m-%d')" });List<User> userList = jdbcDao.queryList(criteria);

表别名支持

有些时候,就算单表操作也必须用到表别名,例如oracle中的xmltype类型。可以在Criteria中设置表别名:

Criteria criteria = Criteria.select(Table.class).tableAlias("t").addSelectFunc("[xmlFile].getclobval() xmlFile")        .where("tableId", new Object[]{10000002L});Object obj = jdbcDao.queryForObject(criteria);//对应的sqlselect t.XML_FILE.getclobval() xmlFile from TABLE t where t.TABLE_ID = ?

使用注解

可以用注解来指定表名、主键、列名或者忽略某个属性。具体的用法下面代码一看就能明白,唯一需要注意的是注解是在getter方法上而不是属性上:

@Table(name = "USER_A", pkField = "userId", pkColumn = "USER_ID")public class AnnotationUser extends Pageable {    /** 用户id */    private Long              userId;    /** 数据库关键字 */    private String            desc;    /** 修改时间 数据库无 */    private Date              gmtModify;        //略...    @Column(name = "`DESC`")    public String getDesc() {        return desc;    }    @Transient    public Date getGmtModify() {        return gmtModify;    }}

执行自定义sql

在实际的应用中,一些复杂的查询如联表查询、子查询等是省不了的。鉴于这类sql的复杂性和所需要的各类优化,通用dao并没有直接封装而是提供了执行自定义sql的接口。

执行自定义sql支持两种方式:直接传sql执行和mybatis方式执行。

###直接传sql执行

该方式可能会让除了dao层之外的业务层出现sql代码,因此是不推荐的,它适合一些不在项目中的情况。

何为不在项目中的情况?例如做一个开发自用的小工具,临时处理一批业务数据等这类后期不需要维护的代码。

要执行自定义sql首先需要在jdbcDao中注入sqlFactory,这里使用SimpleSqlFactory

<bean id="jdbcDao" class="com.dexcoder.dal.spring.JdbcDaoImpl">    <property name="jdbcTemplate" ref="jdbcTemplate"/>    <property name="sqlFactory" ref="sqlFactory"/></bean><bean id="sqlFactory" class="com.dexcoder.dal.SimpleSqlFactory"></bean>

然后就可以直接传入sql执行了:

List<Map<String, Object>> list = jdbcDao.queryRowMapListForSql("select * from USER where login_name = ?",    new Object[] { "selfly_a99" });

这个实现比较简单,参数Object数组中不支持复杂的自定义对象。

mybatis方式执行

采用了插件式实现,使用该方式首先添加依赖:

<dependency>    <groupId>com.dexcoder</groupId>    <artifactId>dexcoder-dal-batis</artifactId>    <version>${version}</version></dependency>

之后同样注入sqlFactory,把上面的SimpleSqlFactory替换成BatisSqlFactoryBean

<bean id="jdbcDao" class="com.dexcoder.dal.spring.JdbcDaoImpl">    <property name="jdbcTemplate" ref="jdbcTemplate"/>    <property name="sqlFactory" ref="sqlFactory"/></bean><bean id="sqlFactory" class="com.dexcoder.dal.batis.BatisSqlFactoryBean">    <property name="sqlLocation" value="user-sql.xml"/></bean>

BatisSqlFactoryBean有一个sqlLocation属性,指定自定义的sql文件,因为使用了spring的解析方式,所以可以和指定spring配置文件时一样使用各类通配符。

user-sql.xml是一个和mybatis的mapper类似的xml文件:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper        PUBLIC "-//dexcoder.com//DTD Mapper 2.0//EN"        "http://www.dexcoder.com/dtd/batis-mapper.dtd"><mapper namespace="User">    <sql id="columns">        user_id,login_name,password,user_age,user_type    </sql>    <select id="getUser">        select        <include refid="columns"/>        from user        <where>            <if test="params[0] != null and params[0].userType != null">                user_type = #{params[0].userType}            </if>            <if test="params[1] != null">                and login_name in                <foreach collection="params[1]" index="index" item="item" separator="," open="(" close=")">                    #{item}                </foreach>            </if>        </where>    </select></mapper>

然后使用代码调用:

User user = new User();user.setUserType("1");Object[] names = new Object[] { "selfly_a93", "selfly_a94", "selfly_a95" };List<Map<String, Object>> mapList = jdbcDao.queryRowMapListForSql("User.getUser", "params", new Object[] { user, names });for (Map<String, Object> map : mapList) {    System.out.println(map.get("userId"));    System.out.println(map.get("loginName"));}

我们调用queryForSql方法时传入了三个参数:

  • User.getUser 具体的sql全id,namespace+id。
  • params 自定义sql中访问参数的key,如果不传入默认为parameters
  • Object[] sql中用到的参数。访问具体参数时可以使用parameters[0],parameters[1]对应里面相应的元素,支持复杂对象。

可以看到这里支持复杂参数,第一个是Userbean对象,第二个是Object数组,至于获取方式可以看上面的xml代码。

除了传入的参数为Object数组并使用parameters[0]这种方式访问相应的元素外,其它的和mybatis可以说是一样的,mybatis支持的动态sql方式这里也可以支持,因为他本身就是来源于mybatis。

另外返回结果中map的key做了LOGIN_NAME到骆驼命名法loginName的转换。

###项目结构说明

dexcoder-commons 一些通用的工具类,里面的maven依赖可以按需添加。

dexcoder-dal 通用dal的接口,这里对于数据库访问没有具体的实现。具体的数据库操作取决于选择的实现方式(目前只有Spring JdbcTemplate)。

dexcoder-dal-spring Spring JdbcTemplate的dal实现。

dexcoder-dal-batis mybatis方式执行sql实现。

dexcoder-test 测试工程

###一些说明

BatisSqlFactory方式由分析了mybatis源码后,提取使用了大量mybatis的代码。

JdbcDao在声明时可以根据需要注入其它几个参数:

<bean id="jdbcDao" class="com.dexcoder.dal.spring.JdbcDaoImpl">    <property name="jdbcTemplate" ref="jdbcTemplate"/>    <property name="sqlFactory" ref="..."/>    <property name="mappingHandler" ref="..."/>    <property name="rowMapperClass" value="..."/>    <property name="dialect" value="..."/></bean>
  • mappingHandler 默认使用DefaultMappingHandler,即遵守上面的约定优于配置,如果需要自定义可以实现该接口。
  • sqlFactory 执行自定义sql时注入相应的sqlFactory。
  • rowMapperClass 默认使用了spring的BeanPropertyRowMapper.newInstance(clazz),需要自定义可以自行实现,标准spring的RowMapper实现即可。
  • dialect 数据库方言,为空会自动判断,一般不需要注入。

很多人问如果我要实现一个自己的dao是不是要继承这个dao然后实现自己的方法?其实不用,自己实现的dao按spring标准的JdbcTemplate路子走就可以,完全无耦合。另外自己实现的dao这个通用的分页组件仍然可以一样使用。

其它主要包含的组件

感谢&相关链接

分页部分参考使用了Mybatis通用分页插件: https://github.com/pagehelper/Mybatis-PageHelper

博客:http://www.dexcoder.com/selfly

作者邮箱: [email protected]

交流QQ群: 32261424


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap