文章

01.MyBatis3入门

01.MyBatis3入门

MyBatis 入门

什么是 MyBatis?

  • MyBatis 是一款优秀的持久层框架
  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程
  • MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 【Plain Old Java Objects,普通的 Java 对象】映射成数据库中的记录。
  • MyBatis 本是 apache 的一个开源项目 ibatis, 2010 年这个项目由 apache 迁移到了 google code,并且改名为 MyBatis 。
  • 2013 年 11 月迁移到Github .
  • Mybatis 官方文档 : http://www.mybatis.org/mybatis-3/zh/index.html
  • GitHub : https://github.com/mybatis/mybatis-3

为什么需要 MyBatis?

  • Mybatis 就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据
  • 传统的 jdbc 操作 , 有很多重复代码块 .比如 : 数据取出时的封装 , 数据库的建立连接等等… , 通过框架可以减少重复代码,提高开发效率
  • MyBatis 是一个半自动化的ORM 框架 (Object Relationship Mapping) –>对象关系映射
  • 所有的事情,不用 Mybatis 依旧可以做到,只是用了它,所有实现会更加简单!技术没有高低之分,只有使用这个技术的人有高低之别

MyBatis 的优点:

  • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个 jar 文件 + 配置几个 sql 映射文件就可以了,易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
  • 灵活:mybatis 不会对应用程序或者数据库的现有设计强加任何影响。sql 写在 xml 里,便于统一管理和优化。通过 sql 语句可以满足操作数据库的所有需求。
  • 解除 sql 与程序代码的耦合:通过提供 DAO 层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql 和代码的分离,提高了可维护性。
  • 提供 xml 标签,支持编写动态 sql。

MyBatis HelloWorld

  • 测试数据库准备:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CREATE DATABASE `mybatis`; -- 创建数据库“mybatis”;

USE `mybatis`; -- 使用`mybatis`;

DROP TABLE IF EXISTS `user`; -- 如果存在则删除表`用户`;

CREATE TABLE `user` (
`id` int(20) NOT NULL,
`name` varchar(30) DEFAULT NULL,
`pwd` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 创建表`用户`(
-- `id` int ( 20 ) NOT NULL ,
-- `name` varchar ( 30 )默认为NULL ,
-- `pwd` varchar ( 30 )默认为NULL ,
-- 主键(`id`)
-- ) 引擎= InnoDB默认字符集= utf8;


insert  into `user`(`id`,`name`,`pwd`) values (1,'hacket','123456'),(2,'张三','abcdef'),(3,'李四','987654');
-- 插入` user `(`id`,`name`,`pwd`)值( 1 , 'hacket' , '123456' ) , ( 2 , '张三' , 'abcdef' ),( 3 , '李四' , '987654' );
  • 导入 MyBatis 相关 jar 包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<dependencies>
	<!--junit module-->
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<scope>test</scope>
		<version>4.13.1</version>
	</dependency>
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.47</version>
	</dependency>
	<dependency>
		<groupId>org.mybatis</groupId>
		<artifactId>mybatis</artifactId>
		<version>3.5.16</version>
	</dependency>
</dependencies>
  • 编写 MyBatis 核心配置文件:resources/mybatis-config.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
	PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="zfs1314520"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/userMapper.xml"/>
    </mappers>
</configuration>
  • 编写 MyBatis 工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory = null;
    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    // 获取SqlSession连接
    public static SqlSession getSession() {
        return sqlSessionFactory.openSession();
    }
}
  • 实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
public class User {
    private int id;  //id
    private String name;   //姓名
    private String pwd;   //密码
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}
  • UserMapper 接口
1
2
3
4
public interface UserMapper {
    //查询所有用户
    List<User> selectUser();
}
  • 编写 resources/userMapper.xml 配置文件,并在 resources/mybatis-config.xml 中配置
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="me.hacket.mapper.UserMapper">
    <select id="selectUser" resultType="me.hacket.model.User">
        select *
        from user
    </select>
</mapper>

注意: namespace 不能写错,值为 UserMapper 类的全路径

  • 编写测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyTest {
    @Test
    public void selectUser() {
        SqlSession session = MybatisUtils.getSession();
        //方法一:
        //List<User> users = session.selectList("com.kuang.mapper.UserMapper.selectUser");
        //方法二:
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.selectUser();
        for (User user : users) {
            System.out.println(user);
        }
        session.close();
    }
}

namespace

  1. 将上面案例中的 UserMapper 接口改名为 UserDao;
  2. 将 userMapper.xml 中的 namespace 改为为 UserDao 的路径 .
  3. 再次测试

配置文件中 namespace 中的名称为对应 Mapper 接口或者 Dao 接口的完整包名,必须一致

MyBatis CRUD XML

select

select 标签

  • select 标签是 mybatis 中最常用的标签之一
  • select 语句有很多属性可以详细配置每一条 SQL 语句
    • SQL 语句返回值类型。【完整的类名或者别名】
    • 传入 SQL 语句的参数类型 。【万能的 Map,可以多尝试使用】
    • 命名空间中唯一的标识符
    • 接口中的方法名与映射文件中的 SQL 语句 ID 一一对应
    • id
    • parameterType
    • resultType

单个参数

根据 id 查询用户

  • 在 UserMapper 中添加对应方法
1
2
3
4
public interface UserMapper {
   //根据id查询用户
   User selectUserById(int id);
}
  • userMapper.xml 中添加 Select 语句
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="me.hacket.mapper.UserMapper">
    <select id="selectUserById" resultType="me.hacket.model.User">
        select *
        from user
        where id = #{id}
    </select>
</mapper>
  • 测试类中测试
1
2
3
4
public interface UserMapper {
	// 根据id查询用户
    User selectUserById(@Param("id") int id2);
}

多个参数

根据 密码 和 名字 查询用户

方式一:@Param

  1. 在接口方法的参数前加 @Param 属性
  2. Sql 语句编写的时候,直接取 @Param 中设置的值即可,不需要单独设置参数类型
  • UserMapper.java
1
2
3
4
5
6
public interface UserMapper {
	// 2个参数:通过用户名和密码查询用户
	// username和pwd是传入的参数,@Param("username")和@Param("pwd")是给参数起别名,方便在mapper.xml中占位
	// 如 select * from user where name = #{username} and pwd = #{pwd}
	User selectUserByNameAndPassword(@Param("username") String username, @Param("pwd") String pwd);
}
  • userMapper.xml
1
2
3
4
5
6
<select id="selectUserByNameAndPassword" resultType="me.hacket.model.User">
	select *
	from user
	where name = #{username}
	  and pwd = #{pwd}
</select>

方式二:Map

  • UserMapp.java 接口方法中,参数直接传递 Map
1
2
3
4
public interface UserMapper {
	 // 多个参数:用Map
	User selectUserByNameAndPassword2(@Param("params") Map<String, Object> map);
}
  • userMapper.xml 编写 sql 语句的时候,需要传递参数类型,参数类型为 map(大小写不区分)
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="me.hacket.mapper.UserMapper">
    <select id="selectUserByNameAndPassword2" parameterType="mAP" resultType="me.hacket.model.User">
        select *
        from user
        where name = #{params.username}
          and pwd = #{params.pwd}
    </select>
</mapper>
  • 在使用方法的时候,Map 的 key 为 sql 中取的值即可,没有顺序要求
1
2
3
4
5
6
7
8
9
10
11
@Test
public void testSelectUserByNameAndPassword2() {
	SqlSession session = MybatisUtils.getSession();
	UserMapper mapper = session.getMapper(UserMapper.class);
	Map<String, Object> map = new HashMap<>();
	map.put("username", "hacket");
	map.put("pwd", "123456");
	User user = mapper.selectUserByNameAndPassword2(map);
	System.out.println(user);
	session.close();
}

总结: 如果参数过多,我们可以考虑直接使用 Map 实现,如果参数比较少,直接传递参数即可

insert

使用 insert 标签进行插入操作,它的配置和 select 标签差不多。

  • UserMapping.java
1
2
// 添加一个用户
int addUser(User user);
  • userMappper.xml
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="me.hacket.mapper.UserMapper">
    <insert id="addUser" parameterType="me.hacket.model.User">
        insert into user (id, name, pwd)
        values (#{id}, #{name}, #{pwd})
    </insert>
</mapper>
  • 测试
1
2
3
4
5
6
7
8
9
10
@Test
public void testAddUser() {
	SqlSession session = MybatisUtils.getSession();
	UserMapper mapper = session.getMapper(UserMapper.class);
	User user = new User(5, "xiaosheng", "zfs123456");
	int i = mapper.addUser(user);
	System.out.println(i);
	session.commit(); //提交事务,重点!不写的话不会提交到数据库
	session.close();
}

注意点:增、删、改操作需要提交事务

update

使用 update 标签进行更新操作,它的配置和 select 标签差不多

  • UserMapper.java
1
int updateUser(User user);
  • userMapper.xml
1
2
3
<update id="updateUser" parameterType="me.hacket.model.User">
	update user set name=#{name},pwd=#{pwd} where id = #{id}
</update>
  • 测试
1
2
3
4
5
6
7
8
9
10
11
@Test
public void testUpdateUser() {
	SqlSession session = MybatisUtils.getSession();
	UserMapper mapper = session.getMapper(UserMapper.class);
	User user = mapper.selectUserById(1);
	user.setPwd("qwert");
	int i = mapper.updateUser(user);
	System.out.println(i);
	session.commit(); //提交事务,重点!不写的话不会提交到数据库
	session.close();
}

delete

  • UserMapper.java
1
2
//根据id删除用户
int deleteUser(int id);
  • userMapper.xml
1
2
3
<delete id="deleteUser" parameterType="int">  
    delete from user where id = #{id}  
</delete>
  • 测试
1
2
3
4
5
6
7
8
9
@Test
public void testDeleteUser() {
	SqlSession session = MybatisUtils.getSession();
	UserMapper mapper = session.getMapper(UserMapper.class);
	int i = mapper.deleteUser(5);
	System.out.println(i);
	session.commit(); //提交事务,重点!不写的话不会提交到数据库
	session.close();
}

小结

  • 所有的增删改操作都需要提交事务;查询不需要
  • 接口所有的普通参数,尽量都写上 @Param 参数,尤其是多个参数时,必须写上
  • 有时候根据业务的需求,可以考虑使用 map 传递参数
  • 为了规范操作,在 SQL 的配置文件中,我们尽量将 Parameter 参数和 resultType 都写上

XML 配置解析

核心配置文件

  • mybatis-config.xml 系统核心配置文件
  • MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
  • 能配置的内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
<!-- 注意元素节点的顺序!顺序不对会报错 -->

可以阅读 mybatis-config.xml 上面的 dtd 的头文件

environments

官方文档很详细: [mybatis – MyBatis 3配置](https://mybatis.org/mybatis-3/zh_CN/configuration.html#environments)
1
2
3
4
5
6
7
8
9
10
11
12
13
<environments default="development">
 <environment id="development">
   <transactionManager type="JDBC">
     <property name="..." value="..."/>
   </transactionManager>
   <dataSource type="POOLED">
     <property name="driver" value="${driver}"/>
     <property name="url" value="${url}"/>
     <property name="username" value="${username}"/>
     <property name="password" value="${password}"/>
   </dataSource>
 </environment>
</environments>
  • 配置 MyBatis 的多套运行环境,将 SQL 映射到多个不同的数据库上,必须指定其中一个为默认运行环境(通过 default 指定)
  • 子元素节点:environment
    • dataSource 数据源是必须配置的;使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源
    • 子元素节点:transactionManager - [ 事务管理器 ]

mappers

  • 映射器 : 定义映射 SQL 语句文件
  • 既然 MyBatis 的行为其他元素已经配置完了,我们现在就要定义 SQL 映射语句了。但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。映射器是 MyBatis 中最核心的组件之一,在 MyBatis 3 之前,只支持 xml 映射器,即:所有的 SQL 语句都必须在 xml 文件中配置。而从 MyBatis 3 开始,还支持接口映射器,这种映射器方式允许以 Java 代码的方式注解定义 SQL 语句,非常简洁。

引入 mapper 资源方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 使用相对于类路径的资源引用 -->
<mappers>
 <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
 <mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>
<!--
使用映射器接口实现类的完全限定类名
需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<mappers>
 <mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
<!--
将包内的映射器接口实现全部注册为映射器
但是需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<mappers>
 <package name="org.mybatis.builder"/>
</mappers>

user-mapper.xml 文件:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="me.hacket.mapper.UserMapper">
    <select id="selectUser" resultType="me.hacket.model.User">
        select * from user
    </select>
</mapper>
  • namespace 的命名必须跟某个接口同名
  • 接口中的方法与映射文件中 sql 语句 id 应该一一对应
  1. namespace 和子元素的 id 联合保证唯一 , 区别不同的 mapper
  2. 绑定 DAO 接口
  3. namespace 命名规则 : 包名 + 类名

properties 优化

数据库这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。

  • 在资源目录下新建一个 db.properties
1
2
3
4
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf8
username=root
password=123456
  • db.properties 替换 mybatis-config.xml 写死的配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<configuration>
   <!--导入properties文件-->
   <properties resource="db.properties"/>

   <environments default="development">
       <environment id="development">
           <transactionManager type="JDBC"/>
           <dataSource type="POOLED">
               <property name="driver" value="${driver}"/>
               <property name="url" value="${url}"/>
               <property name="username" value="${username}"/>
               <property name="password" value="${password}"/>
           </dataSource>
       </environment>
   </environments>
   <mappers>
       <mapper resource="mapper/UserMapper.xml"/>
   </mappers>
</configuration>

typeAliases 优化

类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。

1
2
3
4
<!--配置别名,注意顺序-->
<typeAliases>
   <typeAlias type="me.hacket.model.User" alias="User"/>
</typeAliases>

当这样配置时,User 可以用在任何使用 me.hacket.model.User 的地方。

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

1
2
3
<typeAliases>
   <package name="me.hacket.model"/>
</typeAliases>

每一个在包 me.hacket.model 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。

若有注解,则别名为其注解值。见下面的例子:

1
2
3
4
@Alias("user")
public class User {
  // ...
}

其他 XML 配置

settings 设置

设置(settings)相关 => 查看帮助文档

  • 懒加载方法
  • 缓存开启关闭
  • 日志实现

一个配置完整的 settings 元素的示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<settings>
 <setting name="cacheEnabled" value="true"/>
 <setting name="lazyLoadingEnabled" value="true"/>
 <setting name="multipleResultSetsEnabled" value="true"/>
 <setting name="useColumnLabel" value="true"/>
 <setting name="useGeneratedKeys" value="false"/>
 <setting name="autoMappingBehavior" value="PARTIAL"/>
 <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
 <setting name="defaultExecutorType" value="SIMPLE"/>
 <setting name="defaultStatementTimeout" value="25"/>
 <setting name="defaultFetchSize" value="100"/>
 <setting name="safeRowBoundsEnabled" value="false"/>
 <setting name="mapUnderscoreToCamelCase" value="false"/>
 <setting name="localCacheScope" value="SESSION"/>
 <setting name="jdbcTypeForNull" value="OTHER"/>
 <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

类型处理器

  • 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
  • 你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。【了解即可】

对象工厂

  • MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。
  • 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过有参构造方法来实例化。
  • 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。【了解即可】

日志

Mybatis 内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j
  • JDK logging

具体选择哪个日志实现工具由 MyBatis 的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。

标准日志实现

指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现

1
2
3
<settings>
       <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
Log4j
  • Log4j 是 Apache 的一个开源项目
  • 通过使用 Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI 组件….
  • 我们也可以控制每一条日志的输出格式;
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

@Param

@Param 注解用于给方法参数起一个名字。以下是总结的使用原则:

  • 在方法只接受一个参数的情况下,可以不使用 @Param
  • 在方法接受多个参数的情况下,建议一定要使用 @Param 注解给参数命名。
  • 如果参数是 JavaBean ,则不能使用 @Param
  • 不使用 @Param 注解时,参数只能有一个,并且是 Javabean。

#和$区别

  • #{} 的作用主要是替换预编译语句 (PrepareStatement) 中的占位符? 【推荐使用】
1
2
INSERT INTO user (name) VALUES (#{name});
INSERT INTO user (name) VALUES (?);
  • ${} 的作用是直接进行字符串替换
1
2
INSERT INTO user (name) VALUES ('${name}');
INSERT INTO user (name) VALUES ('kuangshen');

MyBatis 传参方式 XML

单个参数

单个参数的传参比较简单,可以是任意形式的,比如 #{a}#{b} 或者 #{param1}但是为了开发规范,尽量使用和入参时一样

  • Mapper 接口定义
1
UserInfo selectByUserId(String userId);
  • Mapper xml
1
2
3
<select id="selectByUserId" resultType="cn.cb.demo.domain.UserInfo">
	select * from user_info where user_id=#{userId} and status=1
</select>

多个参数

使用索引【不推荐】

多个参数可以使用类似于索引的方式传值,比如 #{param1} 对应第一个参数,#{param2} 对应第二个参数…….

  • Mapper 接口如下:
1
UserInfo selectByUserIdAndStatus(String userId,Integer status);
  • Mapper xml 定义:
1
2
3
<select id="selectByUserIdAndStatus" resultType="cn.cb.demo.domain.UserInfo">
	select * from user_info where user_id=#{param1} and status=#{param2}
</select>

注意:由于开发规范,此种方式不推荐开发中使用

使用 @Param

@Param 这个注解用于指定 key,一旦指定了 key,在 SQL 中即可对应的 key 入参。

  • Mapper 接口
1
UserInfo selectByUserIdAndStatus(@Param("userId") String userId, @Param("status") Integer status);
  • Mapper xml
1
2
3
<select id="selectByUserIdAndStatus" resultType="cn.cb.demo.domain.UserInfo">
	select * from user_info where user_id=#{userId} and status=#{status}
</select>

使用 Map

Mybatis 底层就是将入参转换成 Map,入参传 Map 当然也行,此时 #{key} 中的 key 就对应 Map 中的 key

  • Mapper 接口
1
UserInfo selectByUserIdAndStatusMap(Map<String,Object> map);
  • xml
1
2
3
<select id="selectByUserIdAndStatusMap" resultType="cn.cb.demo.domain.UserInfo">
	select * from user_info where user_id=#{userId} and status=#{status}
</select>
  • 测试代码
1
2
3
4
5
6
7
8
@Test
void contextLoads() {
	Map<String, Object> map=new HashMap<>();
	map.put("userId", "1222");
	map.put("status", 1);
	UserInfo userInfo = userMapper.selectByUserIdAndStatusMap(map);
	System.out.println(userInfo);
}

POJO【推荐】

多个参数可以使用实体类封装,此时对应的 key 就是属性名称,注意一定要有 get 方法

  • 实体类
1
2
3
4
5
@Data
public class UserInfoReq {
    private String userId;
    private Integer status;
}
  • Mapper 接口
1
UserInfo selectByEntity(UserInfoReq userInfoReq);
  • xml
1
2
3
<select id="selectByEntity" parameterType="UserInfo" resultType="UserInfo">
	select * from user_info where user_id=#{userId} and status=#{status}
</select>

List 传参

List 传参也是比较常见的,通常是 SQL 中的 in

  • Mapper 接口
1
List<Blog> queryList( List<String> userIds);
  • xml
1
2
3
4
5
6
<select id="queryList" parameterType="List" resultType="blog">
	select * from blog where author in
	<foreach collection="list" item="item" open="(" separator="," close=")" >
		#{item}
	</foreach>
</select>

collection 可以为 arg0, collection, list

  • 测试代码
1
2
3
4
5
6
7
8
9
10
11
@Test
public void queryListTest() {
	SqlSession session = MybatisUtils.getSession();
	BlogMapper mapper = session.getMapper(BlogMapper.class);
	List<String> ids = new ArrayList<>();
	ids.add("hacket");
	ids.add("秦疆");
	List<Blog> blogs = mapper.queryList(ids);
	System.out.println(blogs);
	session.close();
}

数组传参

这种方式类似 List 传参,依旧使用 foreach 语法。

  • Mapper 接口
1
List<Blog> queryArray(String[] userIds);
  • xml
1
2
3
4
5
6
<select id="queryArray"  resultType="blog">
	select * from blog where author in
	<foreach collection="array" item="item" open="(" separator="," close=")" >
		#{item}
	</foreach>
</select>
  • 测试代码
1
2
3
4
5
6
7
8
9
@Test
public void queryArrayTest() {
	SqlSession session = MybatisUtils.getSession();
	BlogMapper mapper = session.getMapper(BlogMapper.class);
	String[] ids = new String[]{"hacket", "秦疆"};
	List<Blog> blogs = mapper.queryArray(ids);
	System.out.println(blogs);
	session.close();
}

生命周期和作用域

MyBatis 生命周期和作用域是至关重要的,因为错误的使用会导致非常严重的 并发问题

生命周期

SqlSessionFactoryBuilder

  • 为了创建 SqlSessionFacroty
  • 一旦创建 SqlSessionFactory,就不再需要它了
  • 局部变量

SqlSessionFactory

  • 可以视为数据库连接池子
  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或者重新创建另一个实例
  • SqlSessionFactory 的最佳作用域是应用作用域。
  • 最简单的就是使用单例模式或者静态单例模式。

SqlSession

  • 连接到连接池的请求
  • SqlSession 的实例不是线程安全的,因此是不可共享的,因此它的最佳作用域是请求或方法作用域。
  • 用完之后需要关闭,否则会造成资源浪费。try…catch…finally… 语句来保证其正确关闭

最佳作用域

SqlSessionFactoryBuilder : 它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是 方法作用域(也就是局部方法变量)。

SqlSessionFactory :

  • 由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。
  • 因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。

SqlSession : SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try…catch…finally… 语句来保证其正确关闭。所以 SqlSession 的最佳的作用域是请求或方法作用域

流程图

MyBatis 遇到问题

userMapper.xml 找不到

通过 MyBatis 测试达梦数据库过程中,运行测试类的时候,项目报错:java.io.IOException: Could not find resource mapper/userMapper.xml

解决方法 1:

  1. 首先看一下你的 UserMapper.xml 文件是不是放在 src 目录下,如果是那么报错的原因就是:maven 是不会去编译 src 目录下的 xml 文件
  2. 需要在项目的 pom.xml 文件中添加这一段代码即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--在build中配置resouces,来防止资源导出失败问题-->
<build>
	<resources>
		<resource>
			<directory>src/main/resources</directory>
			<includes>
				<include>**/*.properties</include>
				<include>**/*.xml</include>
			</includes>
			<filtering>true</filtering>
		</resource>
		<resource>
			<directory>src/main/java</directory>
			<includes>
				<include>**/*.properties</include>
				<include>**/*.xml</include>
			</includes>
			<filtering>true</filtering>
		</resource>
	</resources>
</build>

解决方法 2:userMapper.xml 放到 resource目录 |300 mybatic-config.xml 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="xxx"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/userMapper.xml"/>
    </mappers>
</configuration>

Ref

本文由作者按照 CC BY 4.0 进行授权