1、简介
1.1 什么是Mybatis
- Mybatis是一款优秀的持久层框架。
- 支持定制化SQL、存储过程以及高级映射。
- 避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
- Mybatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO(Plain Old Java Object,普通老式Java对象)为数据库中的记录。
- MyBatis是apache的一个开源项目iBats。
如何获取Mybatis?
1 2 3 4 5
| <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency>
|
1.2、持久化
数据持久化
- 持久化就是将程序的数据在持久化状态和瞬间状态转化的过程。
- 内存:断电即失。
- 数据库(JDBC),IO文件持久化。
- 生活:冷藏。
为什么需要持久化?
1.3、持久层
学过Dao层、Service层、Controller层…
1.4、为什么需要Mybatis?
- 帮助程序员将数据存入到数据库中。
- 传统JDBC代码太复杂。简化、框架、自动化。
- 更容易上手。技术没有高低之分
- 使用的人比较多
2、第一个Mybatis程序
思路:搭建环境–>导入Mybatis–>编写代码–>测试
2.1、环境搭建
搭建数据库
1 2 3 4 5 6 7 8 9
| CREATE TABLE users( `id` INT PRIMARY KEY, `name` CHAR(18), `passwd` CHAR(18) )ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO users(id,NAME,passwd) VALUES(1,'aaa','123'),(2,'bbb','456'),(3,'ccc','789');
SELECT * FROM users;
|
新建项目
1.新建一个Maven项目
2.删除src文件夹
3.导入maven依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
|
2.2、创建一个模块
注意在xml下,url里的&需要转义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?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://localhost:3306/test?useSSL=true&useUnicode=true&characterEncoding=utf-8"/> <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
| public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory;
static { try { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlSession() { return sqlSessionFactory.openSession(); } }
|
2.3、编写代码
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
| public class User { private int id; private String name; private String passwd;
public User() { }
public User(int id, String name, String passwd) { this.id = id; this.name = name; this.passwd = passwd; }
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPasswd() { return passwd; }
public void setPasswd(String passwd) { this.passwd = passwd; }
@Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", passwd='" + passwd + '\'' + '}'; } }
|
1 2 3
| public interface UserDao { List<User> getUserList(); }
|
- 接口实现类由原来的UserDaoImp转变成一个Mapper配置文件
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="com.jh2ng.dao.UserDao"> <select id="getUserList" resultType="com.jh2ng.pojo.User"> select * from users; </select> </mapper>
|
2.4、测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class UserDaoTest {
@Test public void test(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> userList = mapper.getUserList(); for(User user:userList){ System.out.println(user); }
sqlSession.close(); } }
|
注意1:
is not known to the MapperRegistry.
每一个Mapper.xml都需要在Mybatis核心配置文件中注册
1 2 3
| <mappers> <mapper resource="com/jh2ng/dao/UserMapper.xml"/> </mappers>
|
注意2:
Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource
配置路径正确,却还是提示无法找到Mapper.xml,解决如下
配置文件无法被导出或者生效的解决办法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <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>
|
3、CRUD
把dao改成mapper
注意:增删改需要提交事务
3.1、select
查询语句
- id:对应的namespace中的方法名
- resultType:SQL语句执行的返回值
- parameterType:参数类型
1.编写接口
1 2
| User getUserById(int id);
|
2.编写对应的mapper中的sql语句
1 2 3
| <select id="getUserById" parameterType="int" resultType="com.jh2ng.pojo.User"> select * from users where id=#{id} </select>
|
3.测试
1 2 3 4 5 6 7 8 9 10
| @Test public void getUserById() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); sqlSession.close(); }
|
3.2、insert
1.编写接口
1 2
| int addUser(User user);
|
2.编写对应的mapper中的sql语句
1 2 3 4
| <insert id="addUser" parameterType="com.jh2ng.pojo.User"> insert into users(id,name,passwd) values (#{id},#{name},#{passwd}) </insert>
|
3.测试
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Test public void addUser() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int res = mapper.addUser(new User(4, "ddd", "111")); if (res > 0) { System.out.println("插入成功!"); } sqlSession.commit(); sqlSession.close();
|
3.3、update
1.编写接口
1 2
| int updateUser(User user);
|
2.编写对应的mapper中的sql语句
1 2 3
| <update id="updateUser" parameterType="com.jh2ng.pojo.User"> update users set name=#{name},passwd=#{passwd} where id=#{id} </update>
|
3.测试
1 2 3 4 5 6 7 8 9 10
| @Test public void updateUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.updateUser(new User(4,"fff","123")); sqlSession.commit(); sqlSession.close(); }
|
3.4、delete
1.编写接口
1 2
| int deleteUser(int id);
|
2.编写对应的mapper中的sql语句
1 2 3
| <delete id="deleteUser" parameterType="int"> delete from users where id=#{id} </delete>
|
3.测试
1 2 3 4 5 6 7 8 9 10
| @Test public void deleteUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.deleteUser(4); sqlSession.commit(); sqlSession.close(); }
|
3.5、万能Map
假设我们的实体类或者数据库中的表字段或者参数过多,应该考虑使用Map。
Map传递参数,直接在sql语句中取出key即可。
对象传递参数,直接在sql语句中取对象的属性即可。
只有一个基本类型参数的情况下,可以直接在sql中取到。
只有一个参数可以不用写parameterType
例如通过一个id或者是一个用户名去查询的时候可以忽略不写parameterType
多参数用map或者注解。
比如,有十几个字段的数据库,只想修改其中的几个字段,可以使用Map来实现
1 2
| int updateUser1(Map<String,Object> map);
|
1 2 3
| <update id="updateUser1" parameterType="map"> update users set name=#{username} where id=#{userid} </update>
|
现在sql中的参数和map里的key对应
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Test public void updateUser1() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map map = new HashMap<String, Object>(); map.put("userid",3); map.put("username","abc"); mapper.updateUser1(map); sqlSession.commit(); sqlSession.close(); }
|
3.6、模糊查询
模糊查询实现的几种方式
1.Java代码执行的时候,传递通配符%%
1
| List<User> userList = mapper.getUserByName("%a%");
|
2.在sql拼接中使用通配符
1
| select * from users where name like "%"#{name}"%"
|
第一种不方便,第二种会造成sql注入
3.借助mysql函数
1
| select * from users where name like concat('%',#{name},'%')
|
4、配置解析
4.1、核心配置文件
1 2 3 4 5 6 7 8 9 10 11 12
| properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器)
|
4.2、环境配置(environments)
MyBatis 可以配置成适应多种环境。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
可以有多套environment,每个environment有单独的id,通过environments中default属性进行环境切换。
Mybatis默认的事务管理器是JDBC,连接池:POOLED
4.3、属性(properties)
可以通过properties属性来实现引用配置文件。
属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件【db.properties】中配置这些属性,也可以在 properties 元素的子元素中设置。
编写一个配置文件db.properties
1 2 3 4
| driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/test?useSSL=true&useUnicode=true&characterEncoding=utf-8 username=root password=123456
|
在核心配置文件中引入
1 2 3 4
| <properties resource="db.properties"> <property name="username" value="root"/> <property name="password" value="123"/> </properties>
|
1 2 3 4 5 6
| <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>
|
- 可以直接引入外部文件
- 可以在其中增加一些属性配置
- 如果两个文件有同一个字段,优先使用外部配置文件
4.4、类型别名(typeAliases)
- 类型别名是为Java类型设置一个短的名字。
- 意在降低冗余的全限定类名书写。
1 2 3
| <typeAliases> <typeAlias type="com.jh2ng.pojo.User" alias="User"/> </typeAliases>
|
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean。
扫描实体类的包,它的默认别名为这个类名的首字母小写
1 2 3
| <typeAliases> <package name="com.jh2ng.pojo"/> </typeAliases>
|
在实体类少的时候使用第一种方法
在实体类多的时候使用第二种方法
第一种可以自定义,第二种可以用注解自定义别名,实体类中加注解@Alias("别名")
4.5、设置(settings)


4.6、其他配置
4.7、映射器(mappers)
MapperRegistry:注册绑定mapper文件
方式一:【推荐使用】
1 2 3
| <mappers> <mapper resource="com/jh2ng/dao/UserMapper.xml"/> </mappers>
|
方式二:使用class文件绑定注册
1 2 3
| <mappers> <mapper class="com.jh2ng.dao.UserMapper"/> </mappers>
|
注意点:
- 接口和mapper配置文件的名称必须相同
- 接口和mapper配置文件必须在同一包下
方式三:扫描包进行注册绑定
1 2 3
| <mappers> <package name="com.jh2ng.dao"/> </mappers>
|
注意点:
- 接口和mapper配置文件的名称必须相同
- 接口和mapper配置文件必须在同一包下
4.8、作用域(Scope)和生命周期
不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。

SqlSessionFactoryBuilder
- 一旦创建了 SqlSessionFactory,就不再需要它了
SqlSessionFactory
- 相当于数据库连接池
- 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
- SqlSessionFactory 的最佳作用域是应用作用域
- 最简单的就是使用单例模式或者静态单例模式
SqlSession
- 连接到连接池的一个请求
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
- 用完之后必须关闭,否则资源被占用!

一个mapper代表一个具体业务
5、resultMap(解决属性名和字段名不一致的问题)
5.1、问题
数据库中的字段

测试实体类字段不一样的情况
1 2 3 4
| public class User { private int id; private String name; private String password;
|
测试出现的问题

1 2 3 4 5
| select * from users where id=#{id};
// 完整的sql语句 // 类型处理器 去寻找属性passwd这个字段 select id,name,passwd from users where id=#{id};
|
解决办法:
1 2 3
| <select id="getUserId" resultType="user" parameterType="int"> select id,name,passwd as password from users where id=#{id}; </select>
|
5.2、resultMap
结果集映射
1 2
| id name passwd id name password
|
1 2 3 4 5 6 7 8 9 10 11
| <resultMap id="userMap" type="User"> <result column="id" property="id"/> <result column="name" property="name"/> <result column="passwd" property="password"/> </resultMap>
<select id="getUserId" resultMap="userMap"> select * from users where id=#{id}; </select>
|
resultMap
元素是 MyBatis 中最重要最强大的元素
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了
resultMap
的优秀之处——你完全可以不用显式地配置它们
6、日志
6.1、日志工厂
如果一个数据库操作,出现了异常,需要排错,查看日志是最好的方法。
以前:debug
现在:日志工厂

SLF4J
LOG4J
LOG4J2
JDK_LOGGING
COMMONS_LOGGING
STDOUT_LOGGING
NO_LOGGING
在Mybatis中具体使用哪一个日志实现,在设置中设置
STDOUT_LOGGING 标准日志输出
1 2 3 4
| <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
|

6.2、LOG4J
什么是log4j?
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
- 可以控制每一条日志的输出格式
- 定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
- 可以通过一个配置文件来灵活地进行配置
1.先导入log4j的包
1 2 3 4 5 6
| <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
|
2.log4j.properties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| log4j.rootLogger=DEBUG,console,file
log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target=System.out log4j.appender.console.Threshold = DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/jh2ng.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d[yy-MM-dd]][%c]%m%n
log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
|
3.配置log4j的日志实现
1 2 3 4
| <settings> <setting name="logImpl" value="LOG4J"/> </settings>
|
4.log4j的使用

简单使用
1.在要使用log4j的类中导入包
1
| import org.apache.log4j.Logger;
|
2.日志对象,参数为当前类的class
1
| static Logger logger = Logger.getLogger(UserDaoTest.class);
|
3.日志级别
1 2 3
| logger.info("info:进入了test"); logger.debug("debug:进入了test"); logger.error("error:进入了test");
|