个人博客网:https://wushaopei.github.io/ (你想要这里多有)
一、MyBatis概述
1、mybatis简介
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录.
2、mybatis历史
原是apache的一个开源项目iBatis, 2010年6月这个项目由apache software foundation 迁移到了google code,随着开发团队转投Google Code旗下,ibatis3.x正式更名为Mybatis ,代码于2013年11月迁移到Github(下载地址见后)。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
3、为什么要使用mybatis
MyBatis是一个半自动化的持久化层框架。
jdbc编程---当我们使用jdbc持久化的时候,sql语句被硬编码到java代码中。这样耦合度太高。代码不易于维护。在实际项目开发中会经常添加sql或者修改sql,这样我们就只能到java代码中去修改。
Hibernate和JPA
长难复杂SQL,对于Hibernate而言处理也不容易
内部自动生产的SQL,不容易做特殊优化。
基于全映射的全自动框架,javaBean存在大量字段时无法只映射部分字段。导致数据库性能下降。
对开发人员而言,核心sql还是需要自己优化
sql和java编码分开,功能边界清晰,一个专注业务、一个专注数据。
可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO映射成数据库中的记录。成为业务代码+底层数据库的媒介
4、理解 SqlSession及其子父类的含义!
SqlSession的使用范围
- SqlSession中封装了对数据库的操作,如:查询、插入、更新、删除等。
- SqlSession通过SqlSessionFactory创建。
- SqlSessionFactory是通过SqlSessionFactoryBuilder进行创建。
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,因为SqlSession是通过SqlSessionFactory创建的。所以可以将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。
SqlSessionFactory
SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory。
SqlSession
SqlSession是一个面向用户的接口,sqlSession中定义了数据库操作方法。
每个线程都应该它自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
二、MyBatis的Hello示例程序
1、创建一个数据库和一个单表
drop database if exists mybatis;
create database mybatis;
use mybatis;
## 创建单表
####################################################################
create table t_user(
`id` int primary key auto_increment,
`last_name` varchar(50),
`sex` int
);
insert into t_user(`last_name`,`sex`) values('wsp168',1);
select * from t_user;
2、搭建mybatis开发环境
创建一个java工程
导 入 需 要 的 jar 包:
log4j-1.2.17.jar
mybatis-3.4.1.jar
mysql-connector-java-5.1.7-bin.jar
3、创建Pojo对象User
不调用接口方法,直接映射到xml文件中!
package com.webcode.pojo;
public class User {
private Integer id;
private String lastName;
private Integer sex;
。。。。。。。。。。
}
4、在src目录创建mybatis-config.xml核心配置文件
<?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/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/webcode/pojo/UserMapper.xml"/>
</mappers>
</configuration>
配置文件代码获取方法
5、创建UserMapper.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名称空间
取值一般有两种情况:
一种情况是:使用javaBean的全类名
二种情况是:使用Mapper接口的全类名
-->
<mapper namespace="com.webcode.pojo.User">
<!--
select标签用来配置一个select语句
id 用来配置一个唯一的标识
resultType 是查询完之后一条记录对应转换成为的javaBean对象
#{id}是占位符
-->
<select id="selectUserById" resultType="com.webcode.pojo.User">
select id,last_name lastName,sex from t_user where id = #{id}
</select>
</mapper>
6、传统mybatis的hello world 示例代码
package com.webcode.pojo.test;
import java.io.IOException;
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.webcode.pojo.User;
public class UserTest {
@Test
public void testselectUserById() throws IOException {
//从配置文件读取指定的资源,以输入流的形式返回
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
// 通过sqlSessionFactoryBuilder创建SqlSessionFactory(从输入流获取连接条件,创建连接工厂)
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
System.out.println(sqlSessionFactory);
//从工厂中获取一个连接(看成是数据库连接池即可),相当于 以前的Connection对象,每次用来都用关闭
SqlSession openSession = sqlSessionFactory.openSession();
try {
/*由相对路径映射到xml文件中的id,调动执行sql语句,
并以resultType定义的类型返回到请求端或者是客户端*/
User user = openSession.selectOne("com.webcode.pojo.User.selectUserById",1);
System.out.println(user);
} catch (Exception e) {
e.printStackTrace();
} finally {
openSession.close();//释放资源
}
}
}
打印结果: User [id=1, lastName=wsp168, sex=1]
代码流程:
关于Mybatis的配置文件提示功能:
关于log4j的提示:
如果不想看到,解决方案为:
只要在应用的classpath中创建一个名称为log4j.properties的文件, 文件的具体内容如下:
# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
注意:
7.加入log4j配置
导入‘log4j-1.2.17.jar’包
在工程下的config文件夹中创建 "log4j.properties"文件
\u4E0B\uFF1A
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
打印结果:
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory@61832929
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 1585787493.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e853265]
DEBUG [main] - ==> Preparing: select id,last_name lastName,sex from t_user where id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User [id=1, lastName=wsp168, sex=1]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e853265]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e853265]
DEBUG [main] - Returned connection 1585787493 to pool.
流程图解:
三、传统方式mybatis的增,删,改,查实现
1、UserDao:
package com.webcode.dao;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import com.webcode.pojo.User;
/**
* @author 鮀城小帅
* */
/**
*
* 注意:在创建连接时,底层自动对事务commit做了false定义,而没有提交的事务会自动回滚。
* 在执行CRUD时要手动提交事务,即,在执行语句后,session.commit();
* */
public class UserDao {
private SqlSessionFactory sqlSessionFactory;
/*
* 创建sql会话工厂方法
* */
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
/*
* 根据id查询用户
* */
public User queryUserById(Integer id) {
SqlSession openSession = sqlSessionFactory.openSession();
try {
return openSession.selectOne("com.webcode.pojo.User.selectUserById",id);
} catch (Exception e) {
e.printStackTrace();
} finally {
openSession.close();//释放资源
}
return null;
}
/*
* 查询所有用户
* */
public List<User> queryUsers(){
SqlSession session = sqlSessionFactory.openSession();
try {
return session.selectList("com.webcode.pojo.User.queryUsers");
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();//释放资源
}
return null;
}
/*
* 添加用户
* */
public int saveUser(User user) {
SqlSession session = sqlSessionFactory.openSession();
try {
int result = session.insert("com.webcode.pojo.User.saveUser",user);
session.commit();//提交事务
return result;
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();//释放资源
}
return -1;
}
/*
* 根据id删除用户
* */
public int deleteUserById(Integer id) {
SqlSession openSession = sqlSessionFactory.openSession();
try {
int delete = openSession.delete("com.webcode.pojo.User.deleteUserById",id);
openSession.commit();//手动提交事务
return delete;
} catch (Exception e) {
e.printStackTrace();
} finally {
openSession.close();//释放资源
}
return -1;
}
/*
* 修改用户
* */
public int updateUser(User user) {
SqlSession openSession = sqlSessionFactory.openSession();
try {
int update = openSession.update("com.webcode.pojo.User.updateUsers",user);
openSession.commit();//手动提交事务
return update;
} catch (Exception e) {
// TODO: handle exception
}finally {
openSession.close();//释放资源
}
return -1;
}
}
2、编写UserMapper.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名称空间
取值一般有两种情况:
一种情况是:使用javaBean的全类名
二种情况是:使用Mapper接口的全类名
-->
<mapper namespace="com.webcode.pojo.User">
<!--
select标签用来配置一个select语句
id 用来配置一个唯一的标识
resultType 是查询完之后一条记录对应转换成为的javaBean对象
#{id}是占位符
-->
<select id="selectUserById" resultType="com.webcode.pojo.User">
select id,last_name lastName,sex from t_user where id = #{id}
</select>
<!-- 查询全部的User对象 -->
<select id="queryUsers" resultType="com.webcode.pojo.User">
select id,last_name lastName,sex from t_user
</select>
<!-- parameterTypr参数类型(可选)
useGeneratedKeys="true" 使用(或返回)数据库生成的主键
keyProperty="id" 把数据库返回的主键值注入到bean对象的id属性中
-->
<insert id="saveUser" parameterType="com.webcode.pojo.User" >
<!--
selectKey标签可以定义一些查询语句,这些语句可以选择性地在大标签的sql语句之前或之后执行
我们希望,通过执行一个查询,把最后一次生成的id返回。
order属性配置执行的顺序
BEFORE 之前
AFTER 之后
keyProperty="id" 把数据库返回的主键值注入到bean对象的id属性中
resultType="int" 设置selectKey查询之后返回的类型是什么 int 表示Integer类型
-->
<selectKey order="AFTER" keyProperty="id" resultType="int">
select last_insert_id()
</selectKey>
insert into t_user (`last_name`,`sex`) values(#{lastName},#{sex})
</insert>
<!-- <insert id="saveUser" parameterType="com.webcode.pojo.User" -->
<!-- useGeneratedKeys="true" keyProperty="id"> -->
<!-- insert into t_user (`last_name`,`sex`) values(#{lastName},#{sex}) -->
<!-- </insert> -->
<!-- 根据id删除一个用户 -->
<delete id="deleteUserById" parameterType="int">
delete from t_user where id=#{id}
</delete>
<update id="updateUser" parameterType="com.webcode.pojo.User">
update t_user set last_name = #{lastName},sex=#{sex} where id=#{id}
</update>
</mapper>
映射关系图解:
3、编写UserDao测试
package com.webcode.dao.test;
import static org.junit.Assert.*;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.BeforeClass;
import org.junit.Test;
import com.webcode.dao.UserDao;
import com.webcode.pojo.User;
/**
* @author 鮀城小帅
* */
public class UserDaoTest {
private static UserDao userDao;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("mybatis-config.xml"));
userDao = new UserDao();
userDao.setSqlSessionFactory(sqlSessionFactory);
}
@Test
public void testQueryUserById() {
System.out.println(userDao.queryUserById(1));
}
@Test
public void testQueryUsers() {
userDao.queryUsers().forEach(System.out::println);
}
@Test
public void testSaveUser() {
userDao.saveUser(new User(null, "xxx", 1));
}
@Test
public void testDeleteUserById() {
userDao.deleteUserById(3);
}
@Test
public void testUpdateUser() {
userDao.updateUser(new User(3,"aaa",0));
}
}
4、插入记录并返回主键
打印结果:
5、插入记录并返回主键
<!-- parameterType类型(可选) -->
<insert id="saveUser" parameterType="com.webcode.pojo.User" keyProperty="id" useGeneratedKeys="true" >
<!-- parameterType 参数类型(可选)
useGeneratedKeys="true" 使用(或返回)数据库生成的主键
keyProperty="id" 把数据库返回的主键值注入到bean对象的id属性中
-->
insert into t_user(`last_name`,`sex`) values(#{lastName},#{sex})
</insert>
6、标签的使用
<!-- parameterType类型(可选) -->
<insert id="saveUser" parameterType="com.webcode.pojo.User" >
<!--
selectKey标签可以定义一些查询语句,这些语句可以选择性地在大标签的sql语句之前或之后执行
我们希望,通过执行一个查询,把最后一次生成的id返回。
order属性配置执行的顺序
BEFORE 之前
AFTER 之后
keyProperty="id" 把数据库返回的主键值注入到bean对象的id属性中
resultType="int" 设置selectKey查询之后返回的类型是什么 int 表示Integer类型
-->
<selectKey order="AFTER" keyProperty="id" resultType="int">
select last_insert_id
</selectKey>
insert into t_user(`last_name`,`sex`) values(#{lastName},#{sex})
</insert>
四、Mapper 接口方式的 mybatis 的增,删,改,查实现
1、Mapper 接口编程的命名习惯
- JavaBean对象是 User
- Mapper接口的名称是: UserMapper
- sql语句的配置文件命名: UserMapper.xml
- JavaBean对象是 Book
- Mapper接口的名称是: BookMapper
- sql语句的配置文件命名: BookMapper.xml
2、Mapper接口开发有四个开发规范 必须遵守
1、名称空间必须是接口的全类名
2、方法名必须与id值一致
3、parameterType必须与方法参数类型一致
4、resultType必须与方法返回值一致(是指javaBean对象)
3、编写 Mapper 接口
public interface UserMapper {
public User queryUserById(Integer id);
public List<User> queryUsers();
public int saveUser(User user);
public int deleteUserById(Integer id);
public int updateUser(User user);
}
4、UserMapper.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">
<!--
使用Mapper接口方案,需要注意的第一点事项是:
namespace名称空间值必须是Mapper接口的全类名
-->
<mapper namespace="com.webcode.mapper.UserMapper">
<!--
Mapper接口方案需要注意的第二点事项是:
方法名,必须要和id值一致
Mapper接口方案需要注意的第三点事项是:
返回值类型必须一致
Mapper接口方案需要注意的第四点事项是:
参数类型也必须一致(写就必须一致)
-->
<select id="queryUserById" resultType="com.webcode.pojo.User" parameterType="int">
select id,last_name lastName,sex from t_user where id = #{id}
</select>
<!-- public List<User> queryUsers(); -->
<select id="queryUsers" resultType="com.webcode.pojo.User">
select id,last_name lastName,sex from t_user
</select>
<!-- public int saveUser(User user);
普通类型可以忽略(主要是javaBean对象)
-->
<insert id="saveUser" parameterType="com.webcode.pojo.User" useGeneratedKeys="true" keyProperty="id">
insert into t_user(last_name,sex) values(#{lastName},#{sex})
</insert>
<!-- public int deleteUserById(Integer id); -->
<delete id="deleteUserById">
delete from t_user where id = #{id}
</delete>
<!-- public int updateUser(User user); -->
<update id="updateUser" parameterType="com.webcode.pojo.User">
update t_user set last_name = #{lastName},sex = #{sex} where id = #{id}
</update>
</mapper>
5、编写UserMapper测试
package com.webcode.mapper.test;
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 com.webcode.mapper.UserMapper;
import com.webcode.pojo.User;
import org.junit.BeforeClass;
import org.junit.Test;
public class UserMapperTest {
static SqlSessionFactory sqlSessionFactory;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
@Test
public void testQueryUserById() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper mapper = session.getMapper(UserMapper.class);
System.out.println(mapper.queryUserById(1));
} catch (Exception e) {
e.printStackTrace();
}finally {
session.close();
}
}
@Test
public void testQueryUsers() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.queryUsers().forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
}finally {
session.close();
}
}
@Test
public void testSaveUser() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper mapper = session.getMapper(UserMapper.class);
int saveUser = mapper.saveUser(new User(null,"吴",11));
session.commit();
System.out.println(saveUser);
} catch (Exception e) {
e.printStackTrace();
}finally {
session.close();
}
}
@Test
public void testDeleteUserById() {
SqlSession openSession = sqlSessionFactory.openSession();
try {
UserMapper mapper = openSession.getMapper(UserMapper.class);
int deleteUserById = mapper.deleteUserById(2);
openSession.commit();
System.out.println(deleteUserById);
} catch (Exception e) {
e.printStackTrace();
}finally {
openSession.close();
}
}
@Test
public void testUpdateUser() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper mapper = session.getMapper(UserMapper.class);
int updateUser = mapper.updateUser(new User(1,"少",33));
session.commit();
System.out.println(updateUser);
}catch (Exception e) {
e.printStackTrace();
}finally {
session.close();
}
}
}
五、mybatis的核心配置之properties
<!--
properties标签可以用来定义多组键值对
每个property标签是一组键值对
properties标签还可以引入单独的属性配置文件
resource="jdbc.properties" 引入classpath路径下的jdbc.properties配置文件
源码目录,就是classPath路径
properties标签在引入外部属性配置文件和内部定义属性的时候。外部会替换掉内部定义的值。
-->
<properties resource="jdbc.properties">
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
</properties>