一.MyBatis介绍
1.什么是框架?
框架即一个半成品软件。开发者从头开发一个软件需要花费大量精力,于是有一些项目组开发出半成品软件,开发者在这些软件的基础上进行开发,这样的软件就称之为框架。
如果将开发完成的软件比作是一套已经装修完毕的新房,框架就好比是一套已经修建好的毛坯房。用户直接购买毛坯房,保证建筑质量和户型合理的同时可以进行风格的自由装修。
使用框架开发的好处:
- 省去大量的代码编写、减少开发时间、降低开发难度。
- 限制程序员必须使用框架规范开发,增强代码的规范性,降低程序员之间沟通及日后维护的 成本。
- 将程序员的注意力从技术中抽离出来,更集中在业务层面。
使用框架就好比和世界上最优秀的软件工程师共同完成一个项目,并且他们完成的还是基础、全局的工作。
2.什么是ORM框架?
ORM(Object Relationl Mapping),对象关系映射,即在数据库和对象之间作映射处理。
之前我们使用JDBC操作数据库,必须手动进行数据库和对象间的数据转换。
如下代码:
// 新增方法,将对象转为sql语句字段
public void AddUser(User user) throwsException {
Class.forName("com.mysql.jdbc.Driver");
Connection connection =DriverManager.getConnection("jdbc:mysql://localhost:3306 /mybatis?characterEncoding=utf8","root", "root");
String sql = "INSERT INTO user values(null,?,?,?,?)";
PreparedStatement preparedStatement =connection.prepareStatement(sql);
preparedStatement.setString(1,user.getName());
preparedStatement.setInt(2,user.getAge());
preparedStatement.setString(3,user.getAddress());
preparedStatement.setString(4,user.getSex());
preparedStatement.executeUpdate();
// 省略资源关闭...
}
// 查询方法,将数据库结果集转为对象
public List<User> findAllUser() throws Exception {
Connection connection =DriverManager.getConnection("jdbc:mysql://localhost:3306 /mybatis?characterEncoding=utf-8", "root", "root");
PreparedStatement preparedStatement =connection.prepareStatement("select * from
user");
ResultSet resultSet =preparedStatement.executeQuery();
//遍历查询结果集
List<User> users = new ArrayList<>();
while(resultSet.next()){
// 拿到每一列数据
int id = resultSet.getInt("id");
String name =resultSet.getString("name");
int age = resultSet.getInt("age");
String address =resultSet.getString("address");
String sex =resultSet.getString("sex");
// 将数据封装到对象中
User user = new User();
user.setId(id);
user.setName(name);
user.setAge(age);
user.setAddress(address);
user.setSex(sex);
users.add(user);
}
// 省略资源关闭...
return users;
}
这段代码中,数据库数据与对象数据的转换代码繁琐、无技术含量。而使用ORM框架代替JDBC后,框架可以帮助程序员自动进行转换,只要像平时一样操作对象,ORM框架就会根据映射完成对数据库的操作,极大的增强了开发效率。
3.什么是MyBatis?
MyBatis是一个半自动的ORM框架,其本质是对JDBC的封装。使用MyBatis不需要写JDBC代码,但需要程序员编写SQL语句。之前是apache的一个开源项目iBatis,2010年改名为MyBatis。
补充:
Hibernate也是一款持久层ORM框架,多年前的市场占有率很高,但近年来市场占有率越来越低。
MyBatis与Hibernate的比较:
- MyBatis是一个半自动的ORM框架,需要手写SQL语句。
- Hibernate是一个全自动的ORM框架,不需要手写SQL语句。
- 使用MyBatis的开发量要大于Hibernate。
为什么Hibernate市场占有率越来越低:
对于新手学习Hibernate时间成本比MyBatis大很多,MyBatis上手很快。
Hibernate不需要写SQL语句是因为框架来生成SQL语句。对于复杂查询,开发者很难控制生 成的SQL语句,这就导致SQL调优很难进行。
之前的项目功能简单,数据量小,所以使用Hibernate可以快速完成开发。而近年来项目的 数据量越来越大,而互联网项目对查询速度要求也很高,这就要求我们一定要精细化的调整 SQL语句。此时灵活性更强,手动编写SQL语句的MyBatis慢慢代替了Hibernate使用。
在高并发、大数据、高性能、高响应的互联网项目中,MyBatis是首选的持久框架。而对于对性能要求不高的比如内部管理系统等可以使用Hibernate。
二.MyBatis入门案例
1.环境搭建
1
**(1)将SQL文件导入数据库 **
打开Navicat,右键locahost,新建数据库,
双击mybatis数据库,使其连接,然后右键-->运行SQL文件,选择已有的数据库sql文件导入
可以看见导入成功!右键刷新之后可以看见导入的表。
2
**(2)创建maven工程,引入依赖 **
创建一个空项目
再在此空项目下创建一个不设置Archetype的maven项目,
在 mybatisDemo1工程中的pom.xml文件中配置依赖<dependencies>如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>mybatisDemo1</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--必须引入的包-->
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!--mysql驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!--不是必须引入的包,但是为了方便我们使用而引入-->
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
</project>
(3)创建mybatis核心配置文件SqlMapConfig.xml
主要配置如何连接数据库
放在mybatisDemo1的src/main/resources中
如果"http://mybatis.org/dtd/mybatis-3-config.dtd"url地址报红色,那么就如下解决:
File-->Setting-->搜索DTD,找到Schemas and DTDs,点击加号,将路径添加到其中即可。
SqlMapConfig.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="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
</configuration>
**(4)将log4j.properties文件放入src/main/resources中,让控制台打印SQL语句。 **
log4j.properties文件内容:
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
#log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%d{MM/dd HH:mm:ss}] %-6r [%15.15t] %-5p %30.30c %x - %m\n
**5 **
**(5)创建实体类 **
User.java:
package com.first.pojo;
public class User {
private int id;
private String username;
private String sex;
private String address;
public User() {
}
public User(int id, String username, String sex, String address) {
this.id = id;
this.username = username;
this.sex = sex;
this.address = address;
}
public int getId() {
return id;
}
public void setId(int 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 String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
项目结构:
2.创建持久层接口和映射文件
项目结构:
1
(1)在java目录创建持久层接口
package com.first.mapper;
import com.first.pojo.User;
import java.util.List;
public interface UserMapper {
//每一个方法对应一个sql语句
//接口函数由映射文件实现
List<User> findAll();
}
(2)在resource目录创建UserMapper的
映射文件,注意目录要和对应接口一致,也是com/first/mapper,创建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>后:
<?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 namespace="com.first.mapper.UserMapper">
<!--
id是方法名;
resultType表示结果的对象,即列表的泛型;
查找用select标签 -->
<select id="findAll" resultType="com.first.pojo.User">
select * from user
</select>
</mapper>
(3)将映射文件配置到mybatis核心配置文件SqlMapConfig.xml中 ,因为项目启动时,先加载核心配置文件SqlMapConfig.xml,只有注册到核心配置文件SqlMapConfig.xml中的配置文件才可以加载到。
在SqlMapConfig.xml的<configuration>标签中添加如下代码注册映射文件
<!-- 注册映射文件-->
<mappers>
<mapper resource="com/first/mapper/UserMapper.xml"></mapper>
</mappers>
**映射文件注意事项: **
- 映射文件要和接口名称相同。
- 映射文件要和接口的目录结构相同。
- 映射文件中namespace属性要写接口的全名。
- 映射文件中标签的id属性是接口方法的方法名。
- 映射文件中标签的resultType属性是接口方法的返回值类型。
- 映射文件中标签的parameterType属性是接口方法的参数类型。
- 映射文件中resultType、parameterType属性要写全类名,如果是集合类型,则写其泛型的全类名。
3.测试持久层接口方法
如第二节配置之后,mybatis自己就利用代理对象帮我们实现了接口方法,所以接下来直接测试就行了!
package com.first.mapper;
import com.first.pojo.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 org.junit.Test;
import java.io.InputStream;
import java.util.List;
public class TestUserMapper {
@Test
public void testFinaAll() throws Exception{
//(1)读取核心配置文件
InputStream is= Resources.getResourceAsStream("SqlMapConfig.xml");
//(2)创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//(3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
SqlSessionFactory factory = builder.build(is);
//(4)SqlSessionFactory对象获取SqlSession对象
SqlSession session = factory.openSession();
//(5)SqlSession对象获取代理代理对象
UserMapper userMapper = session.getMapper(UserMapper.class);
//(6)代理对象执行方法
List<User> all=userMapper.findAll();
//(7)释放资源
session.close();
is.close();
}
}
点击左侧绿色按钮开始测试:
遇到一个异常:
Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration
java.io.IOException: Could not find resource com/first/mapper/UserMapper.xml
找了找发现我之前测试类命名错误了(命名成UserMapper),所以我修改了名称为TestUserMapper,但是idea自动帮我把UserMapper.xml里的UserMapper也改成了TestUserMapper,但是发现还是不行,于是参考了下面的文章解决了bug!
解决mybatis中:Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration_水巷石子的博客-CSDN博客
运行结果:
4.MyBatis核心对象及工作流程
*MyBatis***核心对象 **
SqlSessionFactoryBuilder
SqlSession工厂构建者对象,使用构造者模式创建SqlSession工厂对象。
SqlSessionFactory
SqlSession工厂,使用工厂模式创建SqlSession对象。
SqlSession
该对象可以操作数据库,也可以使用动态代理模式创建持久层接口的代理对象操作数据库。
Mapper
持久层接口的代理对象,他具体实现了持久层接口,用来操作数据库。
*MyBatis***工作流程 **
1
- 创建SqlSessionFactoryBuilder对象
- SqlSessionFactoryBuilder对象构建了SqlSessionFactory对象:构造者模式
- SqlSessionFactory对象生产了SqlSession对象:工厂模式
- SqlSession对象创建了持久层接口的代理对象:动态代理模式
- 代理对象操作数据库
5.使用SqlSession操作数据库
除了代理对象能够操作数据库,SqlSession也能操作数据库。只是这种方式在开发中使用的较少(了解即可),接下来我们使用SqlSession操作数据库:
在TestUserMapper中添加一个测试函数testFinaAll2(),如下:
package com.first.mapper;
import com.first.mapper.UserMapper;
import com.first.pojo.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 org.junit.Test;
import java.io.InputStream;
import java.util.List;
public class TestUserMapper {
@Test
public void testFinaAll() throws Exception{
//(1)读取核心配置文件
InputStream is= Resources.getResourceAsStream("SqlMapConfig.xml");
//(2)创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//(3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
SqlSessionFactory factory = builder.build(is);
//(4)SqlSessionFactory对象获取SqlSession对象
SqlSession session = factory.openSession();
//(5)SqlSession对象获取代理代理对象
UserMapper userMapper = session.getMapper(UserMapper.class);
//(6)代理对象执行方法
List<User> all=userMapper.findAll();
all.forEach(System.out::println);
//(7)释放资源
session.close();
is.close();
}
@Test
public void testFinaAll2() throws Exception{
//(1)读取核心配置文件
InputStream is= Resources.getResourceAsStream("SqlMapConfig.xml");
//(2)创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//(3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
SqlSessionFactory factory = builder.build(is);
//(4)SqlSessionFactory对象获取SqlSession对象
SqlSession session = factory.openSession();
//(5)SqlSession对象直接操作数据库
//参数是持久层接口全类名+方法名
List<User> users=session.selectList("com.first.mapper.UserMapper.findAll");
users.forEach(System.out::println);
//(6)释放资源
session.close();
is.close();
}
}
输出和上节一样。
6.Mapper动态代理原理
接下来我们通过源码,了解MyBatis的Mapper对象究竟是怎么生成的,他又是如何代理接口的方法。
**获取代理对象 **
点开测试类的 getMapper 方法,查看该方法最终调用了什么方法。
当看到 Proxy.newProxyInstance 时,可以确定 getMapper 方法最终调用的是JDK动态代理方法,且使用MapperProxy类定义代理方式
**查看代理方式 **
点开MapperProxy类,查看invoke方法,查看代理对象是如何工作的。
可以看到,MapperProxy调用了MapperMethod的execute方法定义了代理方式,且底层调用的是SqlSession的方法,根据映射文件标签不同调用不同的SqlSession方法。
结论:
- SqlSession的getMapper方法,最终是调用的是JDK动态代理方法,生成一个代理对象,类型就是传入的接口类型。
- MapperProxy对象通过调用MapperMethod的execute方法定义了代理方式,该方法的底层调用的是SqlSession的方法。
版权归原作者 深海鱼肝油ya 所有, 如有侵权,请联系我们删除。