GitHub:https://github.com/yihonglei/MyBatis-Study
MyBatis真正的强大,在于其映射语句的魔力。
SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):
在我们描述每个元素细节之前,我们先感受一些元素的使用实例,以下实例分为UserMapper.XML配置的XML版本
和Java注解版本,也可以根据实际情况两种都用,但是同名方法只能用XML或Java注解。
项目构建:https://blog.csdn.net/yhl_jxy/article/details/51941545
UserMapper.java接口修改为如下内容:
package com.lanhuigu.mybatis.mapper;
import com.lanhuigu.mybatis.entity.User;
import com.lanhuigu.mybatis.param.UserReq;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
/**
* Mapper接口
* @author yihonglei
* @date 2018/11/20 19:00
*/
public interface UserMapper {
//=======================XML版=======================
/**
* xml实现方式
* @author yihonglei
* @date 2018/11/20 19:00
*/
User queryUserById(int id);
/**
* 多个参数
*/
User queryUserByIdAndUserName(@Param("id") int id, @Param("userName") String userName);
/**
* Map参数封装
*/
User queryUserByMap(Map map);
/**
* 对象参数封装
*/
User queryUserByPojo(UserReq req);
/**
* 查询用户对象--返回多个--List
*/
List queryUserList();
/**
* 查询用户对象--返回map
*/
Map queryUserResultMap(int id);
/**
* 查询用户对象--返回多个--List
UserMapper.xml修改为如下内容:
insert into user (id,userName,age)
values(#{id},#{userName},#{age})
update user set userName=#{userName}, age=#{age}
where id=#{id}
delete from user where id=#{id}
MyBatisXMLConfigTest.java
package com.lanhuigu.mybatis;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import com.lanhuigu.mybatis.entity.User;
import com.lanhuigu.mybatis.mapper.UserMapper;
/**
* MyBatis XML配置测试
* @author yihonglei
* @date 2018/11/21 17:34
*/
public class MyBatisXMLConfigTest {
@Test
public void testMyBatis() {
String resource = "mybatis-config.xml";
SqlSessionFactory sqlSessionFactory = null;
SqlSession session = null;
try {
// 读取资源文件
InputStream is = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
session = sqlSessionFactory.openSession();
// mapper调用
UserMapper userMapper = session.getMapper(UserMapper.class);
//================XML测试=================
// 查询--select--返回User对象
User user = userMapper.queryUserById(1);
System.out.println("user="+user);
System.out.println(user.getUserName());
// 查询--select--返回多个User对象--List
/*List list = userMapper.queryUserList();
for(int i=0;i userMap = userMapper.queryUserResultMap(1);
System.out.println("userMap="+userMap);
System.out.println(userMap.get("USERNAME"));*/
// 查询--select--返回List>
/*List> list = userMapper.queryUserResultListMap();
System.out.println("ListMap="+list);
for (int i=0;i userMap = list.get(i);
System.out.println(userMap.get("USERNAME"));
}*/
// 插入--insert
/*User user = new User();
user.setId(3);
user.setUsername("three");
user.setAge(25);
int result = userMapper.insertUser(user);
if (result > 0) {
System.out.println("添加用户成功");
// 事务提交
session.commit();
} else {
System.out.println("添加用户失败");
// 事务回滚
session.rollback();
}*/
// 更新--update
/*User user = new User();
user.setId(3);
user.setUsername("this three update");
user.setAge(25);
int result = userMapper.updateUser(user);
if (result > 0) {
System.out.println("更新成功");
// 事务提交
session.commit();
} else {
System.out.println("更新失败");
// 事务回滚
session.rollback();
}*/
//删除--delete
/*int result = userMapper.deleteUser(3);
if (result > 0) {
System.out.println("删除成功");
// 事务提交
session.commit();
} else {
System.out.println("删除失败");
// 事务回滚
session.rollback();
}*/
//=================注解测试===================
/*
* 只需将xml测试代码copy一份,然后将方法名都加上一个New即可
* 在UserMapper.java映射器中原本可以将xml部分注释,
* 注解部分方法用同名,但是这样做我们就得将UserMapper.xml删掉,
* 或者改名,否则注解不能查询出数值,着跟命名空间和class,xml加载有关系.
* 总之class中和xml中不能出现注解和xml同时使用,对于单个方法,二者
* 只能存在其一,否则mybatis不知道你到底想干啥,只能什么都不做了
*/
//eg.
/*User user = userMapper.queryUserByIdNew(1);
System.out.println("user="+user);
System.out.println(user.getUserName());*/
} catch (Exception e) {
if (session != null) {
session.close();
}
}
}
}
上面是一个MyBatis映射文件的配置实例,下面对配置文件做详细说明。
查询语句是MyBatis中最常用的元素,作为一个持久层框架,把数据放入数据库是一个方面,
把数据取一条或多条更是重中之重。
这个语句称之为queryUserById,接受一个int或Integer的参数,
返回一个com.lanhuigu.mybatis.entity.User的对象,这个如果嫌写起来长,
可以在mybatis-config.xml中配置别名,这个地方直接用别名替代。
1.1、别名配置
1.2、select语句中参数id符号:
#{id}获取传入的参数。
这样MyBatis处理时底层用JDBC生产预编译语句,JDBC底层MyBatis替我们做了,
这样我们只需关注MyBatis的应用,避免写大量的JDBC代码,提高工作效率,调用时直接执行。
1.3、select元素有很多属性可供选择
1.4、select元素相关属性的描述
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。 |
parameterMap | 这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。 |
resultType | 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。 |
resultMap | 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用。 |
flushCache | 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。 |
useCache | 将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。 |
fetchSize | 这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。 |
statementType | STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
resultSetType | FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)。 |
databaseId | 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
resultOrdered | 这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。 |
resultSets | 这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。 |
数据插入(insert)、数据更新(update)、数据删除(delete)实现大同小异。
insert into user (id,userName,age)
values(#{id},#{userName},#{age})
update user set userName=#{userName}, age=#{age}
where id=#{id}
delete from user where id=#{id}
这三个元素的属性描述如下:
属性 | 描述 |
---|---|
id | 命名空间中的唯一标识符,可被用来代表这条语句。 |
parameterType | 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。 |
parameterMap | 这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。 |
flushCache | 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句)。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。 |
statementType | STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys | (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。 |
keyProperty | (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 |
keyColumn | (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 |
databaseId | 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。
它可以被静态地(在加载参数) 参数化. 不同的属性值通过包含的实例变化。
把上面的实例中UserMainMapper.xml修改,来个简单实例:
id = #{id}
将查询语句的条件由where id=#{id}修改成sql语句块替代。
这样整个xml中条件部分共同的地方都提出来,避免重复啰嗦。
当然,sql语句块除了可以用于条件部分,还可以用于其它任地方,
id = #{id}
userName,
age
查询时,效果一样。
总结:
1)sql语句块用于提出xml共同sql
2)sql语句块可以置于xml文件中查询语句前后,位置无关系,都能调用。
MyBatis的参数功能是非常强大的,前面用到的sql中,如果parameterType="int"
只是进行一个简单的整数传入,但是,在插入语句中,我们传入的是类型User对象:
insert into user (id,userName,age)
values(#{id},#{userName},#{age})
如果 User 类型的参数对象传递到了语句中,id、userName 和 password 属性将会被查找,然后将它们的值传入预处理语句
的参数中。这点对于向语句中传参是比较好的而且又简单,不过参数映射的功能远不止于此。
首先,像 MyBatis 的其他部分一样,参数也可以指定一个特殊的数据类型。
#{property,javaType=int,jdbcType=NUMERIC}
像 MyBatis 的剩余部分一样,javaType 通常可以从参数对象中来去确定,前提是只要对象不是一个 HashMap。
那么 javaType 应该被确定来保证使用正确类型处理器。NOTE 如果 null 被当作值来传递,对于所有可能为空的列,
JDBC Type 是需要的。你可以自己通过阅读预处理语句的 setNull() 方法的 JavaDocs 文档来研究这种情况。
为了以后定制类型处理方式,你也可以指定一个特殊的类型处理器类(或别名),比如:
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
尽管看起来配置变得越来越繁琐,但实际上是很少去设置它们。对于数值类型,还有一个小数保留位数的设置,
来确定小数点后保留的位数。
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}
最后,mode 属性允许你指定 IN,OUT 或 INOUT 参数。如果参数为 OUT 或 INOUT,参数对象属性的真实值将会被改变,
就像你在获取输出参数时所期望的那样。如果 mode 为 OUT(或 INOUT),而且 jdbcType 为 CURSOR
(也就是 Oracle 的 REFCURSOR),你必须指定一个 resultMap 来映射结果集到参数类型。
要注意这里的 javaType 属性是可选的,如果左边的空白是 jdbcType 的 CURSOR 类型,
它会自动地被设置为结果集。
#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}
MyBatis 也支持很多高级的数据类型,比如结构体,但是当注册 out 参数时你必须告诉它语句类型名称。
比如(再次提示,在实际中要像这样不能换行):
#{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentResultMap}
尽管所有这些强大的选项很多时候你只简单指定属性名,其他的事情 MyBatis 会自己去推断,最多你需要为
可能为空的列名指定 jdbcType。
#{firstName}
#{middleInitial,jdbcType=VARCHAR}
#{lastName}
#{middleInitial,jdbcType=VARCHAR}
#{lastName}
一个完整的配置xml:
insert into user (
id ,
userName ,
age)
values(
#{id,jdbcType=INTEGER},
#{userName,jdbcType=VARCHAR},
#{age,jdbcType=INTEGER}
)
在上面中,很多地方用到#{},默认情况下,使用#{}格式的语法会导致MyBatis创建预处理语句属性并安全地设置值(比如?)。
这样做更安全,更迅速,通常也是首选做法,不过有时你只是想直接在SQL语句中插入一个不改变的字符串,
或者在条件放一个字符串。比如:
id = ${id}
以这种方式接受从用户输出的内容并提供给语句中不变的字符串是不安全的,会导致潜在的 SQL 注入攻击,
因此要么不允许用户输入这些字段,要么自行转义并检验。