教程 > mybatis 中文教程 > 阅读:30

mybatis sql 语句构建器——迹忆客-ag捕鱼王app官网

java 程序员面对的最痛苦的事情之一就是在 java 代码中嵌入 sql 语句。这通常是因为需要动态生成 sql 语句,不然我们可以将它们放到外部文件或者存储过程中。如你所见,mybatis 在 xml 映射中具备强大的 sql 动态生成能力。但有时,我们还是需要在 java 代码里构建 sql 语句。此时,mybatis 有另外一个特性可以帮到你,让你从处理典型问题中解放出来,比如加号、引号、换行、格式化问题、嵌入条件的逗号管理及 and 连接。确实,在 java 代码中动态生成 sql 代码真的就是一场噩梦。例如:

string sql = "select p.id, p.username, p.password, p.full_name, "
"p.last_name,p.created_on, p.updated_on "  
"from person p, account a "  
"inner join department d on d.id = p.department_id "  
"inner join company c on d.company_id = c.id "  
"where (p.id = a.id and p.first_name like ?) "  
"or (p.last_name like ?) "  
"group by p.id "  
"having (p.last_name like ?) "  
"or (p.first_name like ?) "  
"order by p.id, p.full_name";

ag捕鱼王app官网的解决方案

mybatis 3 提供了方便的工具类来帮助解决此问题。借助 sql 类,我们只需要简单地创建一个实例,并调用它的方法即可生成 sql 语句。让我们来用 sql 类重写上面的例子:

private string selectpersonsql() {
  return new sql() {{
    select("p.id, p.username, p.password, p.full_name");
    select("p.last_name, p.created_on, p.updated_on");
    from("person p");
    from("account a");
    inner_join("department d on d.id = p.department_id");
    inner_join("company c on d.company_id = c.id");
    where("p.id = a.id");
    where("p.first_name like ?");
    or();
    where("p.last_name like ?");
    group_by("p.id");
    having("p.last_name like ?");
    or();
    having("p.first_name like ?");
    order_by("p.id");
    order_by("p.full_name");
  }}.tostring();
}

这个例子有什么特别之处吗?仔细看一下你会发现,你不用担心可能会重复出现的 "and" 关键字,或者要做出用 "where" 拼接还是 "and" 拼接还是不用拼接的选择。sql 类已经为你处理了哪里应该插入 "where"、哪里应该使用 "and" 的问题,并帮你完成所有的字符串拼接工作。


sql 类

这里有一些示例:

// 匿名内部类风格
public string deletepersonsql() {
  return new sql() {{
    delete_from("person");
    where("id = #{id}");
  }}.tostring();
}
// builder / fluent 风格
public string insertpersonsql() {
  string sql = new sql()
    .insert_into("person")
    .values("id, first_name", "#{id}, #{firstname}")
    .values("last_name", "#{lastname}")
    .tostring();
  return sql;
}
// 动态条件(注意参数需要使用 final 修饰,以便匿名内部类对它们进行访问)
public string selectpersonlike(final string id, final string firstname, final string lastname) {
  return new sql() {{
    select("p.id, p.username, p.password, p.first_name, p.last_name");
    from("person p");
    if (id != null) {
      where("p.id like #{id}");
    }
    if (firstname != null) {
      where("p.first_name like #{firstname}");
    }
    if (lastname != null) {
      where("p.last_name like #{lastname}");
    }
    order_by("p.last_name");
  }}.tostring();
}
public string deletepersonsql() {
  return new sql() {{
    delete_from("person");
    where("id = #{id}");
  }}.tostring();
}
public string insertpersonsql() {
  return new sql() {{
    insert_into("person");
    values("id, first_name", "#{id}, #{firstname}");
    values("last_name", "#{lastname}");
  }}.tostring();
}
public string updatepersonsql() {
  return new sql() {{
    update("person");
    set("first_name = #{firstname}");
    where("id = #{id}");
  }}.tostring();
}
方法 描述
  • select(string)
  • select(string...)
开始新的或追加到已有的 select子句。可以被多次调用,参数会被追加到 select 子句。 参数通常使用逗号分隔的列名和别名列表,但也可以是数据库驱动程序接受的任意参数。
  • select_distinct(string)
  • select_distinct(string...)
开始新的或追加到已有的 select子句,并添加 distinct 关键字到生成的查询中。可以被多次调用,参数会被追加到 select 子句。 参数通常使用逗号分隔的列名和别名列表,但也可以是数据库驱动程序接受的任意参数。
  • from(string)
  • from(string...)
开始新的或追加到已有的 from子句。可以被多次调用,参数会被追加到 from子句。 参数通常是一个表名或别名,也可以是数据库驱动程序接受的任意参数。
  • join(string) join(string...)
  • inner_join(string)
  • inner_join(string...)
  • left_outer_join(string)
  • left_outer_join(string...)
  • right_outer_join(string)
  • right_outer_join(string...)
基于调用的方法,添加新的合适类型的 join 子句。 参数可以包含一个由列和连接条件构成的标准连接。
  • where(string)
  • where(string...)
插入新的 where 子句条件,并使用 and 拼接。可以被多次调用,对于每一次调用产生的新条件,会使用 and 拼接起来。要使用 or 分隔,请使用 or()。
or() 使用 or 来分隔当前的 where 子句条件。 可以被多次调用,但在一行中多次调用会生成错误的 sql。
and() 使用 and 来分隔当前的 where子句条件。 可以被多次调用,但在一行中多次调用会生成错误的 sql。由于 where 和 having都会自动使用 and 拼接, 因此这个方法并不常用,只是为了完整性才被定义出来。
  • group_by(string)
  • group_by(string...)
追加新的 group by 子句,使用逗号拼接。可以被多次调用,每次调用都会使用逗号将新的条件拼接起来。
  • having(string)
  • having(string...)
追加新的 having 子句。使用 and 拼接。可以被多次调用,每次调用都使用and来拼接新的条件。要使用 or 分隔,请使用 or()。
  • order_by(string)
  • order_by(string...)
追加新的 order by 子句,使用逗号拼接。可以多次被调用,每次调用会使用逗号拼接新的条件。
  • limit(string)
  • limit(int)
追加新的 limit 子句。 仅在 select()、update()、delete() 时有效。 当在 select() 中使用时,应该配合 offset() 使用。(于 3.5.2 引入)
  • offset(string)
  • offset(long)
追加新的 offset 子句。 仅在 select() 时有效。 当在 select() 时使用时,应该配合 limit() 使用。(于 3.5.2 引入)
  • offset_rows(string)
  • offset_rows(long)
追加新的 offset n rows 子句。 仅在 select() 时有效。 该方法应该配合 fetch_first_rows_only() 使用。(于 3.5.2 加入)
  • fetch_first_rows_only(string)
  • fetch_first_rows_only(int)
追加新的 fetch first n rows only 子句。 仅在 select() 时有效。 该方法应该配合 offset_rows() 使用。(于 3.5.2 加入)
delete_from(string) 开始新的 delete 语句,并指定删除表的表名。通常它后面都会跟着一个 where 子句!
insert_into(string) 开始新的 insert 语句,并指定插入数据表的表名。后面应该会跟着一个或多个 values() 调用,或 into_columns() 和 into_values() 调用。
  • set(string)
  • set(string...)
对 update 语句追加 "set" 属性的列表
update(string) 开始新的 update 语句,并指定更新表的表名。后面都会跟着一个或多个 set() 调用,通常也会有一个 where() 调用。
values(string, string) 追加数据值到 insert 语句中。第一个参数是数据插入的列名,第二个参数则是数据值。
into_columns(string...) 追加插入列子句到 insert 语句中。应与 into_values() 一同使用。
into_values(string...) 追加插入值子句到 insert 语句中。应与 into_columns() 一同使用。
add_row() 添加新的一行数据,以便执行批量插入。(于 3.5.2 引入)

提示 注意,sql 类将原样插入 limit、offset、offset n rows 以及 fetch first n rows only 子句。换句话说,类库不会为不支持这些子句的数据库执行任何转换。 因此,用户应该要了解目标数据库是否支持这些子句。如果目标数据库不支持这些子句,产生的 sql 可能会引起运行错误。

从版本 3.4.2 开始,你可以像下面这样使用可变长度参数:

public string selectpersonsql() {
  return new sql()
    .select("p.id", "a.username", "a.password", "p.full_name", "d.department_name", "c.company_name")
    .from("person p", "account a")
    .inner_join("department d on d.id = p.department_id", "company c on d.company_id = c.id")
    .where("p.id = a.id", "p.full_name like #{name}")
    .order_by("p.id", "p.full_name")
    .tostring();
}
public string insertpersonsql() {
  return new sql()
    .insert_into("person")
    .into_columns("id", "full_name")
    .into_values("#{id}", "#{fullname}")
    .tostring();
}
public string updatepersonsql() {
  return new sql()
    .update("person")
    .set("full_name = #{fullname}", "date_of_birth = #{dateofbirth}")
    .where("id = #{id}")
    .tostring();
}

从版本 3.5.2 开始,你可以像下面这样构建批量插入语句:

public string insertpersonssql() {
  // insert into person (id, full_name)
  //     values (#{mainperson.id}, #{mainperson.fullname}) , (#{subperson.id}, #{subperson.fullname})
  return new sql()
    .insert_into("person")
    .into_columns("id", "full_name")
    .into_values("#{mainperson.id}", "#{mainperson.fullname}")
    .add_row()
    .into_values("#{subperson.id}", "#{subperson.fullname}")
    .tostring();
}

从版本 3.5.2 开始,你可以像下面这样构建限制返回结果数的 select 语句,:

public string selectpersonswithoffsetlimitsql() {
  // select id, name from person
  //     limit #{limit} offset #{offset}
  return new sql()
    .select("id", "name")
    .from("person")
    .limit("#{limit}")
    .offset("#{offset}")
    .tostring();
}
public string selectpersonswithfetchfirstsql() {
  // select id, name from person
  //     offset #{offset} rows fetch first #{limit} rows only
  return new sql()
    .select("id", "name")
    .from("person")
    .offset_rows("#{offset}")
    .fetch_first_rows_only("#{limit}")
    .tostring();
}

sqlbuilder 和 selectbuilder (已经废弃)

在版本 3.2 之前,我们的实现方式不太一样,我们利用 threadlocal 变量来掩盖一些对 java dsl 不太友好的语言限制。现在,现代 sql 构建框架使用的构建器和匿名内部类思想已被人们所熟知。因此,我们废弃了基于这种实现方式的 selectbuilder 和 sqlbuilder 类。

下面的方法仅仅适用于废弃的 sqlbuilder 和 selectbuilder 类。

方法 描述
begin() / reset() 这些方法清空 selectbuilder 类的 threadlocal 状态,并准备好构建一个新的语句。开始新的语句时,begin() 是最名副其实的(可读性最好的)。但如果由于一些原因(比如程序逻辑在某些条件下需要一个完全不同的语句),在执行过程中要重置语句构建状态,就很适合使用 reset()。
sql() 该方法返回生成的 sql() 并重置 selectbuilder 状态(等价于调用了 begin() 或 reset())。因此,该方法只能被调用一次!

selectbuilder 和 sqlbuilder 类并不神奇,但最好还是知道它们的工作原理。 selectbuilder 以及 sqlbuilder 借助静态导入和 threadlocal 变量实现了对插入条件友好的简洁语法。要使用它们,只需要静态导入这个类的方法即可,就像这样(只能使用其中的一条,不能同时使用):

import static org.apache.ibatis.jdbc.selectbuilder.*;
import static org.apache.ibatis.jdbc.sqlbuilder.*;

然后就可以像下面这样创建一些方法:

/* 已被废弃 */
public string selectblogssql() {
  begin(); // 重置 threadlocal 状态变量
  select("*");
  from("blog");
  return sql();
}
        
/* 已被废弃 */
private string selectpersonsql() {
  begin(); // 重置 threadlocal 状态变量
  select("p.id, p.username, p.password, p.full_name");
  select("p.last_name, p.created_on, p.updated_on");
  from("person p");
  from("account a");
  inner_join("department d on d.id = p.department_id");
  inner_join("company c on d.company_id = c.id");
  where("p.id = a.id");
  where("p.first_name like ?");
  or();
  where("p.last_name like ?");
  group_by("p.id");
  having("p.last_name like ?");
  or();
  having("p.first_name like ?");
  order_by("p.id");
  order_by("p.full_name");
  return sql();
}

查看笔记

扫码一下
查看教程更方便
网站地图