Mybatis详解(一)
Mybatis简介
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
MyBatis 是支持普通SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集,使开发者只需要关注sql本身。MyBatis 使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
Mybatis架构
1、mybatis配置SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
2、通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
3、由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
4、mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
5、Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
6、Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
7、Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
Mybatis入门
我们先来写一个入门程序,来了解一下Mybatis具体的使用流程,具体的细节会在后面一一讲解。
第一步:创建数据库及数据库表,以供我们来操作。
CREATE DATABASE mybatis;
CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(32) NOT NULL COMMENT '用户名称',
`birthday` DATE DEFAULT NULL COMMENT '生日',
`sex` CHAR(1) DEFAULT NULL COMMENT '性别',
`address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
INSERT INTO `user` VALUES ('1', '王五', NULL, '2', NULL);
INSERT INTO `user` VALUES ('10', '张三', '2014-07-10', '1', '北京市');
INSERT INTO `user` VALUES ('16', '张小明', NULL, '1', '河南郑州');
INSERT INTO `user` VALUES ('22', '陈小明', NULL, '1', '河南郑州');
INSERT INTO `user` VALUES ('24', '张三丰', NULL, '1', '河南郑州');
INSERT INTO `user` VALUES ('25', '陈小明', NULL, '1', '河南郑州');
INSERT INTO `user` VALUES ('26', '王五', NULL, NULL, NULL);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
第二步:创建java项目,创建Mybatis核心配置文件sqlMapConfig.xml
SqlMapConfig.xml是mybatis核心配置文件,配置文件内容为数据源、事务管理
<?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>
<!-- 和spring整合后 environments配置将废除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<transactionManager type="JDBC" />
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
</configuration>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
第三步:创建和数据库表对应的pojo类
public class User implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private Integer id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", sex=" + sex
+ ", birthday=" + birthday + ", address=" + address + "]";
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
第四步:创建sql映射文件User.xml
<?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">
<!-- namespace:命名空间,用于隔离sql,还有一个很重要的作用,后面会讲 -->
<mapper namespace="test">
<!--id为sql语句的id,parameterType为参数类型,resultType为结果类型,#{}为占位符-->
<!--根据id查找用户,#{}里面可以随便写-->
<select id="findUserById" parameterType="Integer" resultType="com.cad.domain.User">
select * from user where id =#{id}
</select>
</mapper>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
第五步:sqlMapConfig配置文件中要引入映射文件
<mappers>
<mapper resource="sqlmap/User.xml"/>
</mappers>
- 1
- 2
- 3
- 4
- 5
- 6
第六步,测试程序,简单的操作
public class MybatisTest {
@Test
public void test() throws IOException {
String resource="config/sqlMapConfig.xml";
//加配置文件
InputStream in=Resources.getResourceAsStream(resource);
//创建SqlSessionFactoryBuilder对象用来创建工厂
SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
//创建工厂
SqlSessionFactory factory=builder.build(in);
//创建Session
SqlSession sqlSession=factory.openSession();
//进行查询
User user=(User)sqlSession.selectOne("test.findUserById",1);
System.out.println(user);
sqlSession.close();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
模糊查询
<select id="findUserByUsername" parameterType="String" resultType="com.cad.domain.User">
<!-- ${}里面只能是value -->
select * from user where username like '%${value}%'
</select>
- 1
- 2
- 3
- 4
- 5
- 6
public class MybatisTest {
@Test
public void test() throws IOException {
String resource="config/sqlMapConfig.xml";
InputStream in=Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
SqlSessionFactory factory=builder.build(in);
SqlSession sqlSession=factory.openSession();
List<User> users=sqlSession.selectList("findUserByUsername","五");
for(User user:users) {
System.out.println(user);
}
sqlSession.close();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
添加
<!-- 添加操作 -->
<insert id="insertUser" parameterType="com.cad.domain.User">
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
- 1
- 2
- 3
- 4
public class MybatisTest {
@Test
public void test() throws IOException {
String resource="config/sqlMapConfig.xml";
InputStream in=Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
SqlSessionFactory factory=builder.build(in);
SqlSession sqlSession=factory.openSession();
User user=new User();
user.setUsername("齐天大圣");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("花果山");
sqlSession.insert("insertUser", user);
sqlSession.commit();
sqlSession.close();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
添加并返回数据库自增主键
<!-- 保存用户 -->
<insert id="saveUser" parameterType="cn.itcast.mybatis.pojo.User">
<!-- selectKey 标签实现主键返回 -->
<!-- keyColumn:主键对应的表中的哪一列 -->
<!-- keyProperty:主键对应的pojo中的哪一个属性 -->
<!-- order:设置在执行insert语句前执行查询id的sql,还是在执行insert语句之后执行查询id的sql -->
<!-- resultType:设置返回的id的类型 -->
<!--LAST_INSERT_ID():是mysql的函数,返回auto_increment自增列新记录id值。-->
<selectKey keyColumn="id" keyProperty="id" order="AFTER"
resultType="int">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO `user`
(username,birthday,sex,address) VALUES
(#{username},#{birthday},#{sex},#{address})
</insert>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
修改
<!-- 修改操作 -->
<update id="updateUser" parameterType="com.cad.domain.User">
update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
</update>
- 1
- 2
- 3
- 4
public class MybatisTest {
@Test
public void test() throws IOException {
String resource="config/sqlMapConfig.xml";
InputStream in=Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
SqlSessionFactory factory=builder.build(in);
SqlSession sqlSession=factory.openSession();
User user=new User();
user.setId(27);
user.setUsername("天蓬元帅");
user.setBirthday(new Date());
user.setSex("女");
user.setAddress("银河");
sqlSession.update("updateUser", user);
sqlSession.commit();
sqlSession.close();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
删除
<!-- 删除操作 -->
<delete id="deleteUser" parameterType="Integer">
delete from user where id=#{id}
</delete>
- 1
- 2
- 3
- 4
public class MybatisTest {
@Test
public void test() throws IOException {
String resource="config/sqlMapConfig.xml";
InputStream in=Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
SqlSessionFactory factory=builder.build(in);
SqlSession sqlSession=factory.openSession();
sqlSession.delete("deleteUser", 27);
sqlSession.commit();
sqlSession.close();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
Mapper动态代理方式
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象。
1.编写Mapper文件
<?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">
<!-- namespace:命名空间,和Mapper接口的位置一样,这样框架会自动帮我们找到接口 -->
<mapper namespace="com.cad.domain.UserMapper">
<select id="findUserById" parameterType="int" resultType="com.cad.domain.User">
select * from user where id =#{id}
</select>
<!-- 模糊查询 -->
<select id="findUserByUsername" parameterType="String" resultType="com.cad.domain.User">
<!-- ${}里面只能是value -->
select * from user where username like '%${value}%'
</select>
<!-- 添加操作 -->
<insert id="insertUser" parameterType="com.cad.domain.User">
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
<!-- 修改操作 -->
<update id="updateUser" parameterType="com.cad.domain.User">
update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
</update>
<!-- 删除操作 -->
<delete id="deleteUser" parameterType="Integer">
delete from user where id=#{id}
</delete>
</mapper>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
2.创建Mapper接口
public interface UserMapper {
public User findUserById(int id);
public List<User> findUserByUsername(String username);
public void insertUser(User user);
public void deleteUser(Integer id);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
3.核心配置文件中引入映射文件
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
- 1
- 2
- 3
- 4
- 5
4.测试
public class MybatisTest {
@Test
public void test() throws IOException {
String resource="config/sqlMapConfig.xml";
InputStream in=Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
SqlSessionFactory factory=builder.build(in);
SqlSession sqlSession=factory.openSession();
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
User user=userMapper.findUserById(10);
System.out.println(user);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
Mapper接口开发需要遵循以下规范:
1、 Mapper.xml文件中的namespace与mapper接口的类路径相同,否则会找不到接口,也就无法生成代理对象。
2、 Mapper接口方法名和Mapper.xml中定义的每个方法的id相同
3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
- 1
- 2
- 3
- 4
开发中使用这种方式来进行开发,简便快捷,代码复用性高,免去很多重复繁琐代码。
Mybatis一些小细节
通过入门例子可以知道,每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。
从 XML 中构建 SqlSessionFactory
我们可以直接通过加载核心配置文件来创建SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- 1
- 2
- 3
- 4
- 5
不使用XML构建 SqlSessionFactory
//自己实现获取数据源
DataSource dataSource = xxx;
//获取事务
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
configuration 添加了一个映射器类(mapper class)。映射器类是 Java 类,它们包含 SQL 映射语句的注解从而避免了 XML 文件的依赖。不过,由于 Java 注解的一些限制加之某些 MyBatis 映射的复杂性,XML 映射对于大多数高级映射来说仍然是必须的。有鉴于此,如果存在一个对等的 XML 配置文件的话,MyBatis 会自动查找并加载它(这种情况下, BlogMapper.xml 将会基于类路径和 BlogMapper.class 的类名被加载进来)。
了解即可,开发中使用xml格式。
- 1
- 2
- 3
主要类的生命周期
SqlSessionFactoryBuilder,这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了,可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在以保证所有的 XML 解析资源开放给更重要的事情。
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的。
每执行一次请求都应该开启一次sqlSession实例。当然要记得在finally中关闭来节省资源。
Mybatis的核心配置文件
MyBatis 的配置文件包含了MyBatis的设置和属性信息。文档的结构和顺序如下:
properties 属性
settings 设置
typeAliases 类型命名
typeHandlers 类型处理器
objectFactory 对象工厂
plugins 插件
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
databaseIdProvider 数据库厂商标识
mappers 映射器
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
properties(属性)
我们可以将数据源等数据配置在外部文件中,然后引用。
//db.peoperties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
- 1
- 2
- 3
- 4
- 5
- 6
- 7
//引入文件
<properties resource="db.properties"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:
- 在 properties 元素体内指定的属性首先被读取。
- 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
typeAliases(类型别名)
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
<typeAliases>
<!-- 单个别名定义 -->
<typeAlias alias="user" type="cn.cad.pojo.User" />
<!-- 批量别名定义,扫描整个包下的类,别名为类名(大小写不敏感) -->
<package name="cn.itcast.mybatis.pojo" />
</typeAliases>
在mapper文件中就可以使用别名user。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Mybatis已经为许多常见的 Java 类型内建了相应的类型别名。它们都是大小写不敏感的,例如Srting,integer等。
插件(plugins)
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
- 1
- 2
- 3
- 4
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 的发行包中的源代码。 假设你想做的不仅仅是监控方法的调用,那么你应该很好的了解正在重写的方法的行为。 因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏 MyBatis 的核心模块。 这些都是更低层的类和方法,所以使用插件的时候要特别当心
配置环境(environments)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境数据库需要有不同的配置;
不过要记住:尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。
配置环境的关键点:
默认的环境 ID(比如:default=”development”)。
每个 environment 元素定义的环境 ID(比如:id=”development”)。
事务管理器的配置(比如:type=”JDBC”)。
数据源的配置(比如:type=”POOLED”)
- 1
- 2
- 3
- 4
- MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”)
- Mybatis三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”)
我们都是使用默认的,具体的参数意思可以查看官方文档。
映射器(mappers)
映射器配置会告诉了 MyBatis 去哪里找映射文件。
<mapper resource=" " />:使用相对于类路径的资源(现在的使用方式)
<mapper url="file:///var/mappers/AuthorMapper.xml"/>:使用绝对路径,基本不会用
<mapper class="cn.itcast.mybatis.mapper.UserMapper"/>:使用mapper接口类路径。注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
<package name="xxx"/>:注册指定包下的所有mapper接口,此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
<link href="https://****img.cn/release/phoenix/mdeditor/markdown_views-778f64ae39.css" rel="stylesheet">
</div>