Mybatis入门与Mapper动态代理
Mybatis简介
MyBatis 本是 apache 的一个开源项目 iBatis, 2010 年这个项目由 apache software foundation 迁移到了 google code,并且改名为 MyBatis 。2013 年 11 月迁移到 Github。
MyBatis 是一个优秀的持久层框架,它对 jdbc 的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建 connection、创建 statement、手动设置参数、结果集检索等 jdbc 繁杂的过程代码。
Mybatis 通过 xml 或注解的方式将要执行的各种 statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过j ava 对象和 statement 中的 sql 进行映射生成最终执行的 sql 语句,最后由 Mybatis 框架执行 sql 并将结果映射成 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通过MappedStatement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
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);
第二步:创建 java 项目,导入 DTD 约束文件和jar包,配置 Mybatis 核心配置文件sqlMapConfig.xml ,SqlMapConfig.xml 是配置文件内容为数据源、事务管理。(配置文件写在 src 根目录下即可,或者也可以在 src下创建一个文件夹 config,用于统一存放配置文件)
<?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>
<typeAliases>
<package name="com.pngyul.mybatis.pojo" />
</typeAliases>
<!-- 和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?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<!--要引入映射文件 mapper.xml,该文件名字可以随意,但最好与你对应的数据库表名一致
该User0.xml文件在第四步会写
-->
<mappers>
<mapper resource="User0.xml" />
</mappers>
</configuration>
第三步:创建和数据库表对应的 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 + "]";
}
}
第四步:创建 sql 映射文件 Use0.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.pngyul.mybatis.pojo.User">
select * from user where id =#{id}
</select>
</mapper>
第五步:测试程序
@Test
public void findUserById() throws IOException {
// 加载核心配置文件
String resource = "sqlMapConfig.xml";
InputStream in = Resources.getResourceAsStream(resource);
// 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行Sql语句
User user = sqlSession.selectOne("test.findUserById", 10);
System.out.println(user);
}
模糊查询
<!-- 模糊查询 -->
<select id="findUserByUsername" parameterType="String" resultType="com.pngyul.mybatis.pojo.User">
select * from user where username like "%"#{haha}"%"
</select>
<!--sql 语句还有另一种写法-->
<!-- ${}里面只能是value -->
select * from user where username like '%${value}%'
//根据用户名称模糊查询用户列表
@Test
public void findUserByUsername() throws Exception {
String resource = "sqlMapConfig.xml";
InputStream in = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> users = sqlSession.selectList("test.findUserByUsername", "五");
for (User user2 : users) {
System.out.println(user2);
}
}
添加
<!-- 添加操作 -->
<insert id="insertUser" parameterType="com.pngyul.mybatis.pojo.User">
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
@Test
public void test() throws IOException {
String resource="sqlMapConfig.xml";
InputStream in=Resources.getResourceAsStream(resource);
SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession=factory.openSession();
User user=new User();
user.setUsername("pngyul");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("广东");
sqlSession.insert("test.insertUser", user);
sqlSession.commit();
sqlSession.close();
}
添加并返回数据库自增主键
<!-- 保存用户 -->
<insert id="saveUser" parameterType="com.pngyul.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="Integer">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO `user`
(username,birthday,sex,address) VALUES
(#{username},#{birthday},#{sex},#{address})
</insert
@Test
public void insertUser() throws Exception {
String resource = "sqlMapConfig.xml";
InputStream in = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setAddress("广东");
user.setBirthday(new Date());
user.setSex("男");
user.setUsername("ccfff");
int i = sqlSession.update("test.insertUser", user);
sqlSession.commit();
System.err.println(user.getId());
sqlSession.close();
}
修改
<!-- 修改操作 -->
</update>
<delete id="deleteUserById" parameterType="Integer">
delete from user where id=#{id}
</delete>
@Test
public void updateUserById() throws Exception {
String resource = "sqlMapConfig.xml";
InputStream in = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setAddress("广东");
user.setBirthday(new Date());
user.setSex("女");
user.setUsername("ccfff");
user.setId(30);
int i = sqlSession.update("test.updateUserById", user);
sqlSession.commit();
sqlSession.close();
}
删除
<!-- 删除操作 -->
<delete id="deleteUser" parameterType="Integer">
delete from user where id=#{id}
</delete>
@Test
public void test() throws IOException {
String resource="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();
}
原始 Dao 开发方法
简单举个例子,了解一下即可
/1、编写mapper文件,并引入到核心配置文件中
<select id="findUserById" parameterType="Integer"
resultType="User">
select * from user where id = #{v}
</select>
//2、定义一个UserDao接口
public interface UserDao {
User queryUserById(Integer id);
}
//3、UserDao实现类
package com.pngyul.mybatis.dao;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import com.pngyul.mybatis.pojo.User;
public class UserDaoImpl implements UserDao {
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
super();
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User queryUserById(Integer id) {
SqlSession sqlsession = sqlSessionFactory.openSession();
User user = sqlsession.selectOne("test.findUserById", id);
sqlsession.close();
return user;
}
}
//测试
public class Junit {
private SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws Exception {
// 创建SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 加载SqlMapConfig.xml配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 创建SqlsessionFactory
this.sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
@Test
public void testQueryUserById() {
// 创建DAO
UserDao userDao = new UserDaoImpl(this.sqlSessionFactory);
// 执行查询
User user = userDao.queryUserById(10);
System.out.println(user);
}
Mapper 动态代理方式
Mapper接口开发方法只需要程序员编写Mapper接口(相当于 Dao 接口),由 Mybatis 框架根据接口定义创建接口的动态代理对象。
Mapper接口开发需要遵循以下四个规范:
- Mapper.xml 文件中的 namespace 与 mapper 接口的类路径相同,否则会找不到接口,也就无法生成代理对象。
- Mapper 接口方法名和 Mapper.xml 中定义的每个方法的 id 相同
- Mapper接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的 parameterType 的类型相同
- Mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的 resultType 的类型相同
演示 Mapper 动态代理方式步骤:
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.pngyul.mybatis.mapper.UserMapper">
<select id="findUserById" parameterType="int" resultType="com.pngyul.mybatis.pojo.User">
select * from user where id =#{id}
</select>
<!-- 模糊查询 -->
<select id="findUserByUsername" parameterType="String" resultType="com.pngyul.mybatis.mapper.UserMapper">
<!-- ${}里面只能是value -->
select * from user where username like '%${value}%'
</select>
<!-- 添加操作 -->
<insert id="insertUser" parameterType="com.pngyul.mybatis.mapper.UserMapper">
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
<!-- 修改操作 -->
<update id="updateUser" parameterType="com.pngyul.mybatis.mapper.UserMapper">
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>
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);
}
3、核心配置文件中引入映射文件
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
4、测试
public class MybatisTest {
@Test
public void test() throws IOException {
String resource="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(1);
System.out.println(user);
}
}
开发中使用这种方式来进行开发,简便快捷,代码复用性高,免去很多重复繁琐代码。
Mybatis 的 SqlMapConfig.xml 文件说明
MyBatis 的配置文件包含了 MyBatis 的设置和属性信息。文档的结构和顺序如下:
properties 属性
settings 设置
typeAliases 类型命名
typeHandlers 类型处理器
objectFactory 对象工厂
plugins 插件
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
databaseIdProvider 数据库厂商标识
mappers 映射器
下面重点介绍几个:
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
//引入文件
<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>
如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:
- 在 properties 元素体内指定的属性首先被读取。
- 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
typeAliases(类型别名)
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
<typeAliases>
<!-- 单个别名定义 -->
<typeAlias alias="user" type="cn.cad.pojo.User" />
<!-- 批量别名定义,扫描整个包下的类,别名为类名(大小写不敏感) -->
<package name="cn.itcast.mybatis.pojo" />
<package name="其它包" />
</typeAliases>
在mapper文件中就可以使用别名user。
Mybatis 已经为许多常见的 Java 类型内建了相应的类型别名。它们都是大小写不敏感的,例如 string,integer 等。
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映射文件名称相同,且放在同一个目录中。
PS::部分学习资料源于传智播客
推荐阅读
-
Mybaits 源码解析 (十一)----- 设计模式精妙使用:静态代理和动态代理结合使用:@MapperScan将Mapper接口生成代理注入到Spring
-
MyBatis从入门到精通(八):MyBatis动态Sql之foreach标签的用法
-
Java JDK动态代理(AOP)的实现原理与使用详析
-
Spring AOP简介与底层实现机制——动态代理
-
Java使用JDK与Cglib动态代理技术统一管理日志记录
-
IDEA下创建SpringBoot+MyBatis+MySql项目实现动态登录与注册功能
-
荐 [设计模式] 代理模式之 静态代理与动态代理 & Mybatis实例解析
-
DB数据源之SpringBoot+MyBatis踏坑过程(二)手工配置数据源与加载Mapper.xml扫描
-
MyBatis入门(二)—— 输入映射和输出映射、动态sql、关联查询
-
MyBatis开发Dao的原始Dao开发和Mapper动态代理开发