1. Mybatis介绍
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
总之,Mybatis对JDBC访问数据库的过程进行了封装,简化了JDBC代码,解决JDBC将结果集封装为Java对象的麻烦。
下图是MyBatis架构图:
(1)mybatis-config.xml是Mybatis的核心配置文件,通过其中的配置可以生成SqlSessionFactory,也就是SqlSession工厂
(2)基于SqlSessionFactory可以生成SqlSession对象
(3)SqlSession是一个既可以发送SQL去执行,并返回结果,类似于JDBC中的Connection对象,也是Mybatis中至关重要的一个对象。
(4)Executor是SqlSession底层的对象,用于执行SQL语句
(5)MapperStatement对象也是SqlSession底层的对象,用于接收输入映射(SQL语句中的参数),以及做输出映射(即将SQL查询的结果映射成相应的结果)
1.1 为什么要使用MyBatis
思考:在开始之前,思考下如何通过JDBC查询User表中的所有记录,并封装到一个List集合中返回。(演示:准备数据、导包、导入JDBC程序)
使用传统方式JDBC访问数据库:
(1)使用JDBC访问数据库有大量重复代码(比如注册驱动、获取连接、获取传输器、释放资源等);
(2)JDBC自身没有连接池,会频繁的创建连接和关闭连接,效率低;
(3)SQL是写死在程序中,一旦修改SQL,需要对类重新编译;
(4)对查询SQL执行后返回的ResultSet对象,需要手动处理,有时会特别麻烦;
...
使用mybatis框架访问数据库:
(1)Mybatis对JDBC对了封装,可以简化JDBC代码;
(2)Mybatis自身支持连接池(也可以配置其他的连接池),因此可以提高程序的效率;
(3)Mybatis是将SQL配置在mapper文件中,修改SQL只是修改配置文件,类不需要重新编译。
(4)对查询SQL执行后返回的ResultSet对象,Mybatis会帮我们处理,转换成Java对象。
...
总之,JDBC中所有的问题(代码繁琐、有太多重复代码、需要操作太多对象、释放资源、对结果的处理太麻烦等),在Mybatis框架中几乎都得到了解决!!
1.2 MyBatis运行流程
- 加载配置
从配置文件或注解获取配置信息并加载成MappedStatement对象,此对象=传入参数的映射配置+要执行的Sql语句+结果的映射配置
- SQL解析
API接口层接收调用请求时,根据传入的Sql的Id找到对应的MappedStatement,再根据传入的参数对象解析MappedStatement,获得最终要执行的Sql语句和参数
- SQL执行
获取数据库连接,执行Sql解析获得的Sql语句和参数,并返回结果
- 结果映射
将数据库返回结果按映射配置转换成需要的数据类型并返回
2. Mybatis案例
2.1 导入依赖的jar包
<!--mysql驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!--mybatis的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!--添加lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
2.2 建立数据库、表
CREATE DATABASE testdatabase;
USE testdatabase;
CREATE TABLE user(id INT PRIMARY KEY AUTO_INCREMENT, name
VARCHAR(20), sex VARCHAR(5),age INT);
INSERT INTO users(name,sex,age) VALUES('Tom','男',17);
INSERT INTO users(name,sex,age) VALUES('Jack','男',19);
INSERT INTO users(name,sex,age) VALUES('Eva','女',18);
2.3 编辑表所对应的实体类User
2.4 添加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>
<!--环境配置标签-->
<environments default="development">
<!--编辑开发环境-->
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="url" value="jdbc:mysql://localhost:3306/testdatabase?serverTime=Asia/Shanghai" />
<property name="username" value="root" />
<property name="password" value="" />
</dataSource>
</environment>
</environments>
</configuration>
2.5编辑UserDao接口
public interface UserDao {
//查询所有的User列表信息
public List<User> selectAll();
}
2.6 编辑接口实现类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是mybaits映射文件的唯一标识,与接口对应-->
<mapper namespace="com.zs.dao.UserDao">
<!--id 表示接口方法
resultType 返回值结果类型
-->
<select id="selectAll" resultType="com.zs.entity.User">
select * from user
</select>
</mapper>
2.7 mybatis关联映射文件
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
2.8 编辑SqlSessionFactory并从 SqlSessionFactory 中获取 SqlSession
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
public class TestUser {
public static void main(String[] args) throws IOException {
//创建SqlSessionFactory
String resource = "mybatis/mybatis-config.xml";
//加载 mybatis 的配置文件(它也加载关联的映射文件)
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
//从SqlSessionFactory中获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取mapper接口,执行接口方法
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.selectAll();
System.out.println(userList);
}
}
3. 优化
3.1连接数据库的配置单独放在一个 properties 文件中
<property name="driver" value="${jdbc.driverName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
jdbc.driverName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/testdatabase?serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=
3.2 在resources文件夹中导入log4j.properties文件
<!--log4j的依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
log4j.rootLogger=debug,stdout,D,E
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
log4j.appender.D=org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File=C://logs/log.log
log4j.appender.D.Append=true
log4j.appender.D.Threshold=DEBUG
log4j.appender.D.layout=org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
log4j.appender.E=org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File=C://logs/error.log
log4j.appender.E.Append=true
log4j.appender.E.Threshold=ERROR
log4j.appender.E.layout=org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
4. Mybatis 单表CURD操作
public interface UserDao {
//查询所有的User列表信息
public List<User> selectAll();
//根据id查询
public User select(int id);
//添加
public int insert(User user);
//修改
public int update(User user);
//删除
public int delete(int id);
}
<?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是mybaits映射文件的唯一标识,与接口对应-->
<mapper namespace="com.zs.dao.UserDao">
<!--id 表示接口方法
resultType 返回值结果类型
-->
<!--resultMap 当表中的字段与实体类中的属性不能完全匹配时, 则需要使用resultMap映射.
type 对应的是实体类
id 为主键 property 对应的是实体类中的属性名 column 则对应的是数据库表中的列名
result 如果实体类中的属性名和数据库表中的列名相照应时,则可以不写,会自动映射
-->
<resultMap id="Map" type="com.zs.entity.User">
<id property="id" column="id"/>
<result property="name" column="name"/>
</resultMap>
<select id="selectAll" resultMap="Map">
select * from user
</select>
<select id="select" resultMap="Map">
select * from user where id=#{id}
</select>
<insert id="insert">
insert into user (name,sex,age) values (#{name},#{sex},#{age})
</insert>
<update id="update">
update user set name =#{name},sex=#{sex},age=#{age} where id=#{id}
</update>
<delete id="delete">
delete from user where id=#{id}
</delete>
</mapper>
在每次执行业务调用时,都需要编辑SqlSessionFactory并从 SqlSessionFactory 中获取 SqlSession,所以就可以用@Before把这些公共代码块抽取出来
public class TestUser {
private SqlSession session;
@Before
public void before()throws Exception{
Reader reader = Resources.getResourceAsReader("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
session = sqlSessionFactory.openSession();
}
@Test
public void select(){
UserDao userDao = session.getMapper(UserDao.class);
User user = userDao.select(2);
System.out.println(user);
}
@Test
public void insert(){
UserDao userDao = session.getMapper(UserDao.class);
User user = new User();
user.setName("Lisa");
user.setSex("女");
user.setAge(20);
userDao.insert(user);
session.commit();//一定要执行,用于提交当前事务
}
@Test
public void update(){
UserDao userDao = session.getMapper(UserDao.class);
User user = new User();
user.setName("Boom");
user.setSex("男");
user.setAge(18);
user.setId(4);
userDao.update(user);
session.commit();
}
@Test
public void delete(){
UserDao userDao = session.getMapper(UserDao.class);
userDao.delete(4);
session.commit();
}