目录
    • 在 where 条件中使用动态条件
    • 在 update 使用动态更新

    mybatis 令人喜欢的一大特性就是动态 sql。在使用 jdbc 的过程中, 根据条件进行 sql 的拼接是很麻烦且很容易出错的,
    mybatis虽然提供了动态拼装的能力,但这些写xml文件,也确实折磨开发。fluent mybatis提供了更贴合java语言特质的,对程序员友好的fluent拼装能力。

    fluent mybatis动态sql,写sql更爽

    数据准备

    为了后面的演示, 创建了一个 maven 项目 fluent-mybatis-dynamic, 创建了对应的数据库和表

    drop table if exists `student`;
    
    create table `student`
    (
        `id`           bigint(21) unsigned not null auto_increment comment '编号',
        `name`         varchar(20) default null comment '姓名',
        `phone`        varchar(20) default null comment '电话',
        `email`        varchar(50) default null comment '邮箱',
        `gender`       tinyint(2)  default null comment '性别',
        `locked`       tinyint(2)  default null comment '状态(0:正常,1:锁定)',
        `gmt_created`  datetime    default current_timestamp comment '存入数据库的时间',
        `gmt_modified` datetime    default current_timestamp on update current_timestamp comment '修改的时间',
        `is_deleted`   tinyint(2)  default 0,
        primary key (`id`)
    ) engine = innodb
      default charset = utf8mb4
      collate = utf8mb4_0900_ai_ci comment ='学生表';

    代码生成

    使用fluent mybatis代码生成器,生成对应的entity文件

    public class generator {
        static final string url = "jdbc:mysql://localhost:3306/fluent_mybatis?usessl=false&useunicode=true&characterencoding=utf-8";
        /**
         * 生成代码的package路径
         */
        static final string basepackage = "cn.org.fluent.mybatis.dynamic";
    
        /**
         * 使用 test/resource/init.sql文件自动初始化测试数据库
         */
        @beforeall
        static void rundbscript() {
            datasourcecreatorfactory.create("datasource");
        }
    
        @test
        void test() {
            filegenerator.build(nothing.class);
        }
    
        @tables(
            /** 数据库连接信息 **/
            url = url, username = "root", password = "password",
            /** entity类parent package路径 **/
            basepack = basepackage,
            /** entity代码源目录 **/
            srcdir = "src/main/java",
            /** 如果表定义记录创建,记录修改,逻辑删除字段 **/
            gmtcreated = "gmt_created", gmtmodified = "gmt_modified", logicdeleted = "is_deleted",
            /** 需要生成文件的表 ( 表名称:对应的entity名称 ) **/
            tables = @table(value = {"student"})
        )
    
        public static class nothing {
        }
    }

    编译项目,ok,下面我们开始动态sql构造旅程

    在 where 条件中使用动态条件

    在mybatis中,if 标签是大家最常使用的。在查询、删除、更新的时候结合 test 属性联合使用。

    示例:根据输入的学生信息进行条件检索

    • 当只输入用户名时, 使用用户名进行模糊检索;
    • 当只输入性别时, 使用性别进行完全匹配
    • 当用户名和性别都存在时, 用这两个条件进行查询匹配查询

    mybatis动态 sql写法

    <select id="selectbystudentselective" resultmap="baseresultmap" parametertype="com.homejim.mybatis.entity.student">
        select
        <include refid="base_column_list" />
        from student
        <where>
            <if test="name != null and name !=''">
              and name like concat('%', #{name}, '%')
            </if>
            <if test="sex != null">
              and sex=#{sex}
            </if>
        </where>
    </select>

    fluent mybatis动态写法

    @repository
    public class studentdaoimpl extends studentbasedao implements studentdao {
       /**
        * 根据输入的学生信息进行条件检索
        * 1. 当只输入用户名时, 使用用户名进行模糊检索;
        * 2. 当只输入性别时, 使用性别进行完全匹配
        * 3. 当用户名和性别都存在时, 用这两个条件进行查询匹配的用
        *
        * @param name   姓名,模糊匹配
        * @param ismale 性别
        * @return
        */
        @override
        public list<studententity> selectbynameoremail(string name, boolean ismale) {
            return super.defaultquery()
                .where.name().like(name, if::notblank)
                .and.gender().eq(ismale, if::notnull).end()
                .execute(super::listentity);
        }
    }

    fluentmybatis的实现方式至少有下面的好处

    • 逻辑就在方法实现上,不需要额外维护xml,割裂开来
    • 所有的编码通过ide智能提示,没有字符串魔法值编码
    • 编译检查,拼写错误能立即发现

    测试

    @springboottest(classes = appmain.class)
    public class studentdaoimpltest extends test4j {
        @autowired
        studentdao studentdao;
    
        @displayname("只有名字时的查询")
        @test
        void selectbynameoremail_onlyname() {
            studentdao.selectbynameoremail("明", null);
            // 验证执行的sql语句
            db.sqllist().wantfirstsql().eq("" +
                    "select id, gmt_created, gmt_modified, is_deleted, email, gender, locked, name, phone " +
                    "from student " +
                    "where name like ?",
                stringmode.sameasspace);
            // 验证sql参数
            db.sqllist().wantfirstpara().eqreflect(new object[]{"%明%"});
        }
    
        @displayname("只有性别时的查询")
        @test
        void selectbynameoremail_onlygender() {
            studentdao.selectbynameoremail(null, false);
            // 验证执行的sql语句
            db.sqllist().wantfirstsql().eq("" +
                    "select id, gmt_created, gmt_modified, is_deleted, email, gender, locked, name, phone " +
                    "from student " +
                    "where gender = ?",
                stringmode.sameasspace);
            // 验证sql参数
            db.sqllist().wantfirstpara().eqreflect(new object[]{false});
        }
    
        @displayname("姓名和性别同时存在的查询")
        @test
        void selectbynameoremail_both() {
            studentdao.selectbynameoremail("明", false);
            // 验证执行的sql语句
            db.sqllist().wantfirstsql().eq("" +
                    "select id, gmt_created, gmt_modified, is_deleted, email, gender, locked, name, phone " +
                    "from student " +
                    "where name like ? " +
                    "and gender = ?",
                stringmode.sameasspace);
            // 验证sql参数
            db.sqllist().wantfirstpara().eqreflect(new object[]{"%明%", false});
        }
    }

    在 update 使用动态更新

    只更新有变化的字段, 空值不更新

    mybatis xml写法

    <update id="updatebyprimarykeyselective" parametertype="...">
        update student
        <set>
          <if test="name != null">
            `name` = #{name,jdbctype=varchar},
          </if>
          <if test="phone != null">
            phone = #{phone,jdbctype=varchar},
          </if>
          <if test="email != null">
            email = #{email,jdbctype=varchar},
          </if>
          <if test="gender != null">
            gender = #{gender,jdbctype=tinyint},
          </if>
          <if test="gmtmodified != null">
            gmt_modified = #{gmtmodified,jdbctype=timestamp},
          </if>
        </set>
        where id = #{id,jdbctype=integer}
    </update>
    
    

    fluent mybatis实现

    @repository
    public class studentdaoimpl extends studentbasedao implements studentdao {
        /**
         * 根据主键更新非空属性
         *
         * @param student
         * @return
         */
        @override
        public int updatebyprimarykeyselective(studententity student) {
            return super.defaultupdater()
                .update.name().is(student.getname(), if::notblank)
                .set.phone().is(student.getphone(), if::notblank)
                .set.email().is(student.getemail(), if::notblank)
                .set.gender().is(student.getgender(), if::notnull)
                .end()
                .where.id().eq(student.getid()).end()
                .execute(super::updateby);
        }    
    }

    测试

    @springboottest(classes = appmain.class)
    public class studentdaoimpltest extends test4j {
        @autowired
        studentdao studentdao;
    
        @test
        void updatebyprimarykeyselective() {
            studententity student = new studententity()
                .setid(1l)
                .setname("test")
                .setphone("13866668888");
            studentdao.updatebyprimarykeyselective(student);
            // 验证执行的sql语句
            db.sqllist().wantfirstsql().eq("" +
                    "update student " +
                    "set gmt_modified = now(), " +
                    "name = ?, " +
                    "phone = ? " +
                    "where id = ?",
                stringmode.sameasspace);
            // 验证sql参数
            db.sqllist().wantfirstpara().eqreflect(new object[]{"test", "13866668888", 1l});
        }
    }

    choose 标签

    在mybatis中choose when otherwise 标签可以帮我们实现 if else 的逻辑。

    查询条件,假设 name 具有唯一性, 查询一个学生

    • 当 id 有值时, 使用 id 进行查询;
    • 当 id 没有值时, 使用 name 进行查询;
    • 否则返回空

    mybatis xml实现

    <select id="selectbyidorname" resultmap="baseresultmap" parametertype="...">
        select
        <include refid="base_column_list" />
        from student
        <where>
            <choose>
              <when test="id != null">
                and id=#{id}
              </when>
              <when test="name != null and name != ''">
                and name=#{name}
              </when>
              <otherwise>
                and 1=2
              </otherwise>
            </choose>
        </where>
    </select>

    fluent mybatis实现方式

    @repository
    public class studentdaoimpl extends studentbasedao implements studentdao {
    
        /**
         * 1. 当 id 有值时, 使用 id 进行查询;
         * 2. 当 id 没有值时, 使用 name 进行查询;
         * 3. 否则返回空
         */ 
        @override
        public studententity selectbyidorname(studententity student) {
           return super.defaultquery()
               .where.id().eq(student.getid(), if::notnull)
               .and.name().eq(student.getname(), name -> isnull(student.getid()) && notblank(name))
               .and.apply("1=2", () -> isnull(student.getid()) && isblank(student.getname()))
               .end()
               .execute(super::findone).orelse(null);
        }
    }

    测试

    @springboottest(classes = appmain.class)
    public class studentdaoimpltest extends test4j {
        @autowired
        studentdao studentdao;
    
        @displayname("有 id 则根据 id 获取")
        @test
        void selectbyidorname_byid() {
            studententity student = new studententity();
            student.setname("小飞机");
            student.setid(1l);
    
            studententity result = studentdao.selectbyidorname(student);
            // 验证执行的sql语句
            db.sqllist().wantfirstsql().eq("" +
                    "select id, gmt_created, gmt_modified, is_deleted, email, gender, locked, name, phone " +
                    "from student " +
                    "where id = ?",
                stringmode.sameasspace);
            // 验证sql参数
            db.sqllist().wantfirstpara().eqreflect(new object[]{1l});
        }
    
        @displayname("没有 id 则根据 name 获取")
        @test
        void selectbyidorname_byname() {
            studententity student = new studententity();
            student.setname("小飞机");
            student.setid(null);
    
            studententity result = studentdao.selectbyidorname(student);
            // 验证执行的sql语句
            db.sqllist().wantfirstsql().eq("" +
                    "select id, gmt_created, gmt_modified, is_deleted, email, gender, locked, name, phone " +
                    "from student " +
                    "where name = ?",
                stringmode.sameasspace);
            // 验证sql参数
            db.sqllist().wantfirstpara().eqreflect(new object[]{"小飞机"});
        }
    
        @displayname("没有 id 和 name, 返回 null")
        @test
        void selectbyidorname_null() {
            studententity student = new studententity();
            studententity result = studentdao.selectbyidorname(student);
            // 验证执行的sql语句
            db.sqllist().wantfirstsql().eq("" +
                    "select id, gmt_created, gmt_modified, is_deleted, email, gender, locked, name, phone " +
                    "from student " +
                    "where 1=2",
                stringmode.sameasspace);
            // 验证sql参数
            db.sqllist().wantfirstpara().eqreflect(new object[]{});
        }
    }

    参考

    fluent mybatis地址
    fluent mybatis文档
    test4j框架

    到此这篇关于fluent mybatis实现动态sql的文章就介绍到这了,更多相关fluent mybatis 动态sql内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!