SQL 类

MyBatis 提供了一个 SQL 工具类,使用这个工具类,我们可以很方便在 Java 代码动态构建 SQL 语句

String newSql = 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 DID=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();

如上面的代码所示,创建了一个匿名的 SQL 类的子类,在匿名子类的初始化代码块中,调用 SELECT()、FROM() 等方法构建 SQL 语句,这种方式能够很好地避免字符串拼接过程中缺少空格或者偶然间重复出现的 AND 关键字导致的 SOL 语句不正确

除了 SELECT 语句外,SQL 工具类也可以用作构建 UPDATE、INSERT 等语句

@Test
public void testInsertSql() {
String insertSql = new SQL().
INSERT INTO("PERSON").
VALUES("ID, FIRST NAME","#{id}, #{firstName}").
VALUES("LAST NAME","#(lastName}").toString();
System.out.println(insertSal);
} @Test
public void testDeleteSql() {
String deleteSql= new SQL() {{
DELETE FROM("PERSON");
WHERE("ID = #{id)");
}}.toString();
System.out.println(deleteql);
} @Test
public void testUpdateSql() {
String updateSql= new SQL() {{
UPDATE("PERSON");
SET("FIRST NAME = #{firstName}");
WHERE("ID = #{id}");
}}.toString();
System.out.println(updateSql);
}

使用 SQL 工具类的另一个好处是可以很方便地在 Java 代码中根据条件动态地拼接 SQL 语句

public String selectPerson(final String id, final String firstName, final String lastName) {
return new SQL() {{
SELECT("P.ID, P.USERNAME, P.PASSWORD");
SELECT("P.FIRST_NAME, P.LAST NAME");
FROM("PERSON P");
if (id != null) {
WHERE("P.ID=#{id}");
}
if (firstName != null) {
WHERE("P.FIRST_NAME=#{firstName}");
}
if (lastName != null) {
WHERE("P.LAST_NAME=#{lastName}");
}
}}.toString();
}

ScriptRunner

该工具类用于读取脚本文件中的 SQL 语句并执行

public void testscriptRunner() {
try {
Connection connection=DriverManager.getConnection("jdbc:hsqldb:mem:mybatis","sa","");
Scriptrunner scriptRunnermnew ScriptRunner(connection):
scriptrunner.runScript(Resources.getResourceAsReader("create-table.sql"))
} catch (Exception e) {
e.printstackTrace();
}
}

如上面的代码所示,ScriptRunner 工具类的构造方法需要一个 java.sql.Connection 对象作为参数。创建 ScriptRunner 对象后,调用该对象的 runScript 方法即可,该方法接收一个读取 SQL 脚本文件的 Reader 对象作为参数

ScriptRunner 工具类中提供了一些属性,用于控制执行 SQL 脚本的一些行为,代码如下;

public class ScriptRunner{
// SQL异常是否中断程序执行
private boolean stopOnError;
// 是否抛出 SQLWarning 警告
private boolean throwWarning;
//是否自动提交
private boolean autoCommit;
// 属性为 true 时,批量执行文件中的 SQL 语句
//为false时逐条执行 sQL 语句,默认情况下,SQL语句以分号分割
private boolean sendfullScript;
//是否去除Windows系统换行符中的\r
private boolean removecrs;
//设置statement属性是否支持转义处理
private boolean escapeProcessing=true;
// 日志输出位置,默认标准输入输出,即控制台
private PrintWriter logWriter=new PrintWriter(System.out);
// 错误日志输出位置,默认控制台
private PrintWriter errorLoqWriter =new PrintWriter(System.err);
// 脚本文件中 SOL 语句的分隔符,默认为分号
private String delimiter= DEFAULT_DELIMITER;
// 是否支持 SOL 语句分割符,单独占一行
private boolean fullLineDelimiter;
...
}

我们可以直接调用这些属性对应的 Setter 方法来控制 ScriptRunner 工具类执行 SQL 脚本的行为

SqlRunner

MyBatis 提供了一个非常实用的、用于操作数据库的 SqlRunner 工具类,该类对 JDBC 做了很好的封装,结合 SQL 工具类,能够很方便地通过 Java 代码执行 SQL 语句并检索 SQL 执行结果

SglRunner 类提供了几个操作数据库的方法,分别说明如下:

  • SqlRunner#closeConnection: 用于关闭 Connection 对象
  • SqlRunner#selectOne(String sql, Object… args):执行 SELECT 语句,SQL 语句中可以使用占位符,如果 SOL 中包含占位符,则可变参数用于为参数占位符赋值,该方法只返回一条记录,若查询结果行数不等于一,则会抛出 SQLException 异常
  • SqlRunner#selectAll(String sql,Object… args):该方法和 selectOne 方法的作用相同,只不过该方法可以返回多条记录,方法返回值是一个 List 对象,List 中包含多个 Map 对象,每个 Map 对象对应数据库中的一行记录
  • SqlRunner#insert(String sql,Object… args):执行一条 INSERT 语句,插入一条记录
  • SqlRunner#update(String sql,Object… args):更新若干条记录
  • SqlRunner#delete(String sql,Object… args):删除若干条记录
  • SqlRunner#run(String sql):执行任意一条 SQL 语句,最好为 DDL 语句

接下来我们来看一下 SqlRunner 工具类的使用案例,代码如下:

@Test
public void testselectOne() throws SQLException {
SqlRunner sqlRunner=new SqlRunner(connection)
String gryUserSql = new SQL() {{
SELECT("*");
FROM("user");
WHERE("id=?");
}}.toString();
Map<String, Object> resultMap = sqlRunner.selectOne(qryUserSql, Integer.valueOf(1));
System.out.println(JSON.toJSONString(resultMap));
} @Test
public void testDelete() throws SQLException {
SqlRunner sqlRunner=new SqlRunner(connection);
String deleteUserSql = new SQL() {{
DELETE FROM("user");
WHERE("id = ?");
}}.toString();
sqlRunner.delete(deleteUserSql,Integer.valueOf(1));
} @Test
public void testUpdate() throws SQLException {
SqlRunner sqlRunner=new SqlRunner(connection);
String updateUserSql = new SQL() {{
UPDATE("user");
SET("nick_name = ?");
WHERE("id = ?");
}}.toString();
sqlRunner.update(updateUserSql,"Jane",Integer.valueOf(1));
} @Test
public void testInsert() throws SQLException {
SqlRunner sqlRunner=new SqlRunner(connection);
String insertUserSql = new SQL() {{
INSERT INTO("user");
INTO COLUMNS("create_time, name, password, phone, nick_name");
INTO VALUES("?,?,?,?,?");
}}.toString();
sqlRunner.insert(insertUserSql,createTime,"Jane","test","18700000000" "J");
}

MetaObject

MetaObject 是 MyBatis 中的反射工具类,该工具类在 MyBati s源码中出现的频率非常高。使用 MetaObject 工具类,我们可以很优雅地获取和设置对象的属性值

使用 MetaObject 工具类获取 User 对象的属性信息,案例代码如下:

@Test
public void testMetaObject() {
List<order> orders= new ArrayList()(
add(new Order("order20171024010246","《MyBatis源码深度解析》图书"));
add(new Order("order20171024010248","《AngularJs入门与进阶》图书"));
};
User user = new User(orders,"江荣波",3);
MetaObject metaObject = SystemMetaObject.forObject(user);
// 获取第一笔订单的商品名称
System.out.println(metaObject.getValue("orders[0].goodsName"));
// 获取第二笔订单的商品名称
System.out.println(metaObject.getvalue("orders[1].goodsName"));
// 为属性设置值
metaObject.setValue("orders[1].orderNo","order20181113010139");
// 判断 User 对象是否有 orderNo 属性
System.out.println("是否有orderNo属性且orderNo属性有对应的Getter方法:" + metaObject.hasGetter("orderNo"));
// 判断 User 对象是否有 name 属性
System.out.println("是否有name属性且name 属性有对应的Getter方法:" + metaObject.hasGetter("name"));
}

MetaClass

MetaClass 是 MyBatis 中的反射工具类,与 MetaOjbect 不同的是,MetaObject 用于获取和设置对象的属性值,而 MetaClass 则用于获取类相关的信息

@Test
public void testMetaClass() {
MetaClass metaClass =MetaClass.forClass(Orderclass,newDefaultReflectorFactory());
// 获取所有有 Getter 方法的属性名
String[] getterNames = metaClass.getGetterNames();
System.out.println(JSON.toJSONString(getterNames));
// 是否有默认构造方法
System.out.println("是否有默认构造方法:" + metaClass.hasDefaultConstructor());
// 某属性是否有对应的Getter/Setter方法
System.out.printIn("orderNo属性是否有对应的Getter 方法:" + metaClass.hasGetter("orderNo"));
System.out.println("orderNo属性是否有对应的Setter方法:" + metaClass.hasSetter("orderNo"));
System.out.println("orderNo属性类型:" + metaClass.getGetterType("orderNo"));
}

ObjectFactory

ObjectFactory 是 MyBatis 中的对象工厂,MyBatis 每次创建 Mapper 映射结果对象的新实例时,都会使用一个对象工厂实例来完成。ObjectFactory 接口只有一个默认的实现,即 DefaultObjectFactory,默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化

public class ObjectFactoryExample {
@Test
public void testObjectFactory() {
ObjectFactory objectFactory = new DefaultObjectFactory();
List<Integer> list = objectFactory.create(List.class);
Map<String, String> map = objectFactory.create(Map.class);
list.addAll(Arrays.asList(1. 23));
map.put("test","test");
System.out.printin(list);
System.out.println(map);
}
}

MyBatis 中使用 ObjectFactory 实例创建 Mapper 映射结果对象的目的是什么呢?实际上,这是 MyBatis 提供的一种扩展机制。有些情况下,在得到映射结果之前我们需要处理一些逻辑,或者在执行该类的有参构造方法时,在传入参数之前,要对参数进行一些处理,这时我们可以通过自定义 ObjectFactory 来实现。下面是一个自定义 ObjectFactory 的案例,代码如下:

public class CustomObjectfactory extends Defaultobjectfactory{

  @Override
public object create(Class type){
if(type.equals(User.class)) {
//实例化User类
User user=(User)super.create(type);
user.setUuid(UUID.randomUUID().toString());
return user;
}
return super.create(type);
}
}

如上面的代码所示,自定义一个 ObjectFactory 非常简单,我们可以继承 DefaultObjectFactory,然后重写 create 方法即可。自定义 ObjectFactory 后,还需要在MyBatis 主配置文件中通过 标签配置自定义的 ObjectFactory,具体如下:

<objectfactory type="com.blog4java.mybatis.objectfactory.CustomobjectFactory">
<property name="someProperty" value="10"/>
</objectfactory>

ProxyFactory

ProxyFactory 是 MyBatis 的代理工厂,主要用于创建动态代理对象,ProxyFactory接口有两个不同的实现,分别为 CglibProxyFactory 和 JavassistProxyFactory。从实现类的名称可以看出,MyBatis 支持两种动态代理策略,分别为 Cglib 和 Javassist 动态代理。ProxyFactory主要用于实现 MyBatis 的懒加载功能。当开启懒加载后,MyBatis 创建 Mapper 映射结果对象后,会通过 ProxyFactory 创建映射结果对象的代理对象。当我们调用代理对象的 Getter 方法获取数据时,会执行 CglibProxyFactory 或 JavassistProxyFactory 中定义的拦截逻辑,然后执行一次额外的查询

下面是使用 JavassistProxyFactory 创建动态代理对象的案例,代码如下:

public class ProxyFactoryExample {

  @Data
@AllArgsConstructor
private static class Order {
private String orderNo;
private String goodsName;
} @Test
public void testProxyFactory() {
// 创建 ProxyFactory对象
ProxyFactory proxyFactory =new JavassistProxyFactory();
Order order = new Order("gn20170123","《MyBatis源码深度解析》图书");
ObjectFactory objectFactory=new DefaultObjectFactory();
//调用ProxyFactory对象的createProxy()方法创建代理对象
Object proxyOrder=proxyFactory.createProxy(order,
mock(ResultLoaderMap.class),
mock(Configuration.class),
objectfactory,
Arrays.asList(String.class,String.class),
Arrays.asList(order.getorderNo(),order.getGoodsName()));
System.out.println(proxyOrder.getClass());
System.out.println(((Order)proxyOrder).getGoodsName());
}
}

MyBatis 常用工具类的更多相关文章

  1. Maven基础&&Spring框架阶段常用工具类整理

    常用工具类 1.密码加密工具类: package com.itheima.utils; import java.security.MessageDigest; import sun.misc.BASE ...

  2. js常用工具类.

    一些js的工具类 复制代码 /** * Created by sevennight on 15-1-31. * js常用工具类 */ /** * 方法作用:[格式化时间] * 使用方法 * 示例: * ...

  3. IOS开发--常用工具类收集整理(Objective-C)(持续更新)

    前言:整理和收集了IOS项目开发常用的工具类,最后也给出了源码下载链接. 这些可复用的工具,一定会给你实际项目开发工作锦上添花,会给你带来大大的工作效率. 重复造轮子的事情,除却自我多练习编码之外,就 ...

  4. Apache Commons 常用工具类整理

    其实一直都在使用常用工具类,只是从没去整理过,今天空了把一些常用的整理一下吧 怎么使用的一看就明白,另外还有注释,最后的使用pom引入的jar包 public class ApacheCommonsT ...

  5. Android 常用工具类之SPUtil,可以修改默认sp文件的路径

    参考: 1. 利用Java反射机制改变SharedPreferences存储路径    Singleton1900 2. Android快速开发系列 10个常用工具类 Hongyang import ...

  6. 封装一个简单好用的打印Log的工具类And快速开发系列 10个常用工具类

    快速开发系列 10个常用工具类 http://blog.csdn.net/lmj623565791/article/details/38965311 ------------------------- ...

  7. javaweb常用工具类及配置文件备份

    Javaweb常用工具类及配置文件备份   做一个代码备份,以后常用到的. hibernate工具类备份 package com.dly.service; /*  * hibernate获取sessi ...

  8. [C#] 常用工具类——直接在浏览器输出数据

    /// <summary> /// <para> </para> /// 常用工具类——直接在浏览器输出数据 /// <para> ---------- ...

  9. [C#] 常用工具类——加密解密类

    using System; using System.Configuration; using System.Collections.Generic; using System.Text; using ...

  10. C#常用工具类——Excel操作类

    /// 常用工具类——Excel操作类 /// <para> ------------------------------------------------</para> / ...

随机推荐

  1. Spring之丐版IOC实现

    文章目录 IOC控制反转 依赖注入 Bean的自动装配方式 丐版IOC实现 BeanDefinition.java ResourceLoader.java BeanRegister.java Bean ...

  2. 记一道国际赛CTF web题

    这是一篇关于打d3ctf坐牢,无奈去打国际赛的题解. TAMUCTF [Blackbox] 首先打开页面,然后发现一个登陆框 刚开始最先想到就是弱口令登陆,尝试几个后发现登陆不进去. 之后我就换了一个 ...

  3. 2022-12-18:给定一个长度为n的二维数组graph,代表一张图, graph[i] = {a,b,c,d} 表示i讨厌(a,b,c,d),讨厌关系为双向的, 一共有n个人,编号0~n-1, 讨

    2022-12-18:给定一个长度为n的二维数组graph,代表一张图, graph[i] = {a,b,c,d} 表示i讨厌(a,b,c,d),讨厌关系为双向的, 一共有n个人,编号0~n-1, 讨 ...

  4. Golang指针隐式间接引用

    1.Golang指针 在介绍Golang指针隐式间接引用前,先简单说下Go 语言的指针 (Pointer),一个指针可以指向任何一个值的内存地址 它指向那个值的内存地址,在 32 位机器上占用 4 个 ...

  5. 【GiraKoo】Android Studio调试时,提示port无法打开

    Android Studio调试时,提示port无法打开 现象描述 在Android Studio在进行调试时,无法正常运行.App闪退. IDE提示: Error running 'app': Un ...

  6. jmeter跨线程组引用变量的3种方法

    利用BeanShell后置处理程序将参数设置为全局变量,用于跨线程传参(注:1.把提取变量的线程组放到引用变量的线程组前2.在测试计划中勾选"独立运行每个线程组") 方法1(jme ...

  7. 第十三届蓝桥杯c++b组国赛题解(还在持续更新中...)

    试题A:2022 解题思路: 有2022个物品,它们的编号分别是1到2022,它们的价值分别等于它们的编号.也就是说,有2022种物品,物品价值等于物品编号. 从2022个物品种选取10个物品,满足1 ...

  8. WPF 入门笔记 - 03 - 样式基础及控件模板

    原学习路线是按照圣殿骑士的<WPF基础到企业应用系列>的路线走的,但是布局之后直接依赖属性学起来有些僵硬,不太好理解,尝试了文章的前部分内容后放弃,调整为本篇博文内容.笔记路线将按照痕迹g ...

  9. const 使用

    宏定义与const的区别?(概念题是最容易丢分)1. 发生时机不一样: 宏定义发生在预处理时,const关键字发生编译时2. 宏定义仅仅只做了字符串的替换,没有类型检查; const关键字有类型检查, ...

  10. @Repeatable元注解的使用

    @Repeatable注解表明标记的注解可以多次应用于相同的声明或类型,此注解由Java SE 8版本引入.以下示例如何使用此注解: 第一步,先声明一个重复注解类: package org.sprin ...