一.什么是Mybatis?
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
相比于jdbc,mybatis提供了参数映射和结果集映射,使我们对参数设置和对结果集的封装变得更加简洁,而且mybatis还提供了动态sql和关联查询 ,这使得我们能够对sql语句进行灵活的操作。
二.Mybatis入门
快速入门Mybatis只需要以下几个步骤:
- 编写Mybatis的全局配置文件
- 编写Mapper映射文件
- 通过全局配置文件构建SqlSessionFactory
- 通过SqlSessionFactory获取SqlSession
示例:
有一个tb_user表,我们使用mybatis对该表进行操作
1.创建一个maven项目:
2.加入mybatis以及相关的依赖
3.编写User类
package com.fs.entity;
import lombok.Data;
import java.util.Date;
@Data
public class User {
private Integer id;
private String username;
private String password;
private String sex;
private Date birthday;
private String address;
}
4.编写Mapper映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
namespace: 命名空间
-->
<mapper namespace="User">
<!-- 查询所有用户:
resultType: 结果集映射,我们这里查出来是User类型,所以resultType的值给User的全限定名
-->
<select id="selectAll" resultType="com.fs.entity.User">
select * from tb_user
</select>
<!-- 根据id查询用户:
parameterType:输入参数类型, 如果是简单数据类型(八大基本数据类型, String, 日期类型) 可以省略,
如果是自定义的类类型: 不可以省略,必须写类的全限定名
#{}是mybatis的占位符
-->
<select id="selectById" parameterType="int" resultType="com.fs.entity.User">
select * from tb_user where id = #{id}
</select>
<!--添加用户:
参数为自定义的User类型,必须写全限定名,
没有结果集,返回的是首影响的行数
-->
<insert id="addUser" parameterType="com.fs.entity.User">
insert into tb_user (id,username,password,sex,birthday,address) values(#{id},#{username},#{password},#{sex},#{birthday},#{address})
</insert>
<!--根据id删除用户-->
<delete id="delById" parameterType="int">
delete from tb_user where id = #{id}
</delete>
</mapper>
5.编写全局配置文件(mybatis-config.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--开发环境-->
<environments default="mysql">
<environment id="mysql">
<!--事务管理器: JDBC: 使用jdbc管理事务, 需要手动提交事务-->
<transactionManager type="JDBC"/>
<!--dataSource数据源的类型: POOLED 连接池 UNPOOLED: 不带连接池 -->
<dataSource type="POOLED">
<!--数据库四大参数-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/work"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--加载sql映射文件-->
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
6.编写dao接口以及实现类
package com.fs.dao;
import com.fs.entity.User;
import java.io.IOException;
import java.util.List;
public interface UserDao {
List<User> selectAll() throws IOException;
User selectById(int id) throws IOException;
int addUser(User user) throws IOException;
int delById(int id) throws IOException;
}
package com.fs.dao.impl;
import com.fs.dao.UserDao;
import com.fs.entity.User;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class UserDaoImpl implements UserDao {
private SqlSessionFactory sessionFactory;
@Override
public List<User> selectAll() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sessionFactory = sqlSessionFactoryBuilder.build(in);
SqlSession session = sessionFactory.openSession();
List<User> users = session.selectList("selectAll");
session.commit();//提交 不然对数据库的更改无效
session.close();
return users;
}
@Override
public User selectById(int id) throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sessionFactory = sqlSessionFactoryBuilder.build(in);
SqlSession session = sessionFactory.openSession();
User user = session.selectOne("selectById");
session.commit();
session.close();
return user;
}
@Override
public int addUser(User user) throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sessionFactory = sqlSessionFactoryBuilder.build(in);
SqlSession session = sessionFactory.openSession();
int row = session.insert("addUser",user);
session.commit();
session.close();
return row;
}
@Override
public int delById(int id) throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sessionFactory = sqlSessionFactoryBuilder.build(in);
SqlSession session = sessionFactory.openSession();
int row = session.delete("delById",id);
session.commit();
session.close();
return row;
}
}
7.进行测试
package com.fs.dao.impl;
import com.fs.entity.User;
import org.junit.Test;
import java.io.IOException;
import java.util.Date;
import static org.junit.Assert.*;
public class UserDaoImplTest {
private UserDaoImpl userDao = new UserDaoImpl();
@Test
public void selectAll() throws IOException {
System.out.println(userDao.selectAll());
}
@Test
public void selectById() throws IOException {
System.out.println(userDao.selectById(1));
}
@Test
public void addUser() throws IOException {
User user = new User();
user.setId(1);
user.setUsername("张三");
user.setPassword("12345");
user.setSex("男");
user.setBirthday(new Date());
user.setAddress("北京");
userDao.addUser(user);
}
@Test
public void delById() throws IOException {
System.out.println(userDao.delById(1));
}
}
这就是mybatis的原始的dao开发,在dao层实现类中获取sqlsession的代码重复了,而且我们通过sqlsession调用操作数据库的方法,如selectOne,selectList等,需要指定statement的id,不利于程序的维护
这里我们可以使用MybatisUtil封装,来减少重复代码:
package com.fs.util;
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 java.io.IOException;
import java.io.InputStream;
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
private static ThreadLocal<SqlSession> tl = new ThreadLocal<>();
static {
InputStream in = null;
try {
in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static SqlSession openSqlSession(){
SqlSession sqlSession = tl.get();
if(sqlSession == null){
sqlSession = sqlSessionFactory.openSession();
tl.set(sqlSession);
}
return sqlSession;
}
public static void closeSqlSession(){
SqlSession sqlSession = tl.get();
if(sqlSession != null){
sqlSession.commit();
sqlSession.close();
tl.remove();//防止内存泄漏
}
}
//定义泛型方法 <T>: 定义泛型变量 T: 使用泛型变量
// MybatisUtil.getMapper(UserMapper.class);
public static <T> T getMapper(Class<T> clazz){
SqlSession sqlSession = tl.get();
if(sqlSession == null){
sqlSession = openSqlSession();
}
return sqlSession.getMapper(clazz);
}
}
这里关于Mybatis工具类的封装,后面会单独出一篇文章
使用工具类后,我们dao实现类代码就会简化成下面这样:
package com.fs.dao.impl;
import com.fs.dao.UserDao;
import com.fs.entity.User;
import com.fs.util.MybatisUtil;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class UserDaoImpl implements UserDao {
@Override
public List<User> selectAll() throws IOException {
SqlSession session = MybatisUtil.openSqlSession();
List<User> users = session.selectList("selectAll");
MybatisUtil.closeSqlSession();
return users;
}
@Override
public User selectById(int id) throws IOException {
SqlSession session = MybatisUtil.openSqlSession();
User user = session.selectOne("selectById",id);
MybatisUtil.closeSqlSession();
return user;
}
@Override
public int addUser(User user) throws IOException {
SqlSession session = MybatisUtil.openSqlSession();
int row = session.insert("addUser",user);
MybatisUtil.closeSqlSession();
return row;
}
@Override
public int delById(int id) throws IOException {
SqlSession session = MybatisUtil.openSqlSession();
int row = session.delete("delById",id);
MybatisUtil.closeSqlSession();
return row;
}
}
进行测试:
尽管重复代码减少了,但还是有重复代码。
不同的地方在于,调用方法的参数不同,sqlsesion调用的方法不同以及返回值不同
三.Mybatis的Mapper代理模式
Mapper代理模式使用的是动态代理:
在运行时根据接口动态生成代理对象,我们只需要编写Mapper接口,并不需要编写实现类,即使以后我们需要再添加功能,也只需要在接口中添加新的方法,以及在Mapper映射文件中添加新的映射就可以了
3.1Mapper代理模式的实现
要求:
- sql映射文件的namespace必须是对应Mapper接口的全限定名(包.接口名)
- sql映射文件的Statement的id必须是对应方法的方法名
statement的id对应Mapper接口中的方法名:
3.2获取Mapper接口代理对象
SqlSession的getMapper(Class clazz): 根据传递Mapper接口类型,通过反射得到对应Mapper接口代理对象
测试:
这样我们就实现了Mapper代理模式
总结:简单说明了Mybatis的入门以及Mapper代理模式的实现,由于篇幅原因使用的细节以及更深层次的知识,下一篇再进行总结