MyBatis框架总结
一、Mybatis概述:
mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,
而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。
mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中
sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并
返回。
采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我
们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。
1.1、JDBC回顾:
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//通过驱动管理类获取数据库链接
connection = DriverManager
.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "ro
ot", " root");
//定义 sql 语句 ?表示占位符
String sql = "select * from user where username = ?";
//获取预处理 statement
preparedStatement = connection.prepareStatement(sql);
//设置参数,第一个参数为 sql 语句中参数的序号(从 1 开始),第二个参数为设置的参数值
preparedStatement.setString(1, "王五");
//向数据库发出 sql 执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();
//遍历查询结果集
while (resultSet.next()) {
System.out.println(resultSet.getString("id") + "
"+resultSet.getString(" username"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放资源
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
以上代码没有做jdbc抽取封装
1.2、JDBC问题分析:
1、加载数据驱动、数据库链接创建、释放资源频繁造成系统资源浪费从而影响系统性能。如果使用数据库链接池可解决此问题。
2、Sql 语句在代码中硬编码问题,造成不易维护。
3、使用 preparedStatement 向占有位符号传参数存在硬编码,因为 sql 语句的 where 条件不确定,可能多也可能少,修改 sql 还要修改代码,系统不易维护。
4、对结果集解析存在硬编码(查询列名),sql 变化导致解析代码变化,系统不易维护,如果能将数据库记
录封装成 pojo 对象解析比较方便。
1.3Mybatis快速入门:
- 创建一个maven工程。
省略。。。。。 - 在 pom.xml 文件中添加 Mybatis3.4.5 的坐标,如下。
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
3.编写一个实体类User。
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` datetime DEFAULT NULL COMMENT '生日',
`sex` char(1) DEFAULT NULL COMMENT '性别',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8;
private Integer id;
private String username;
private Date birthday;
private Character sex;
private String address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Character getSex() {
return sex;
}
public void setSex(Character sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public User(Integer id, String username, Date birthday, Character sex, String address) {
this.id = id;
this.username = username;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
public User() {
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex=" + sex +
", address='" + address + '\'' +
'}';
}
4.编写持久层接口 IUserDao。
package com.ajth.cn.dao;
import com.ajth.cn.pojo.User;
import java.util.List;
/**
* @author xfd
* @version 1.0
* @date 2021/3/28 0028 22:36
*/
public interface IUserDao {
//查询所有用户
List<User> findAll();
}
5.编写持久层接口(IUserDao)的映射文件 IUserDao.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="com.itheima.dao.IUserDao">
<!-- 配置查询所有操作 -->
<select id="findAll" resultType="com.ajth.cn.pojo.User">
select * from user
</select>
</mapper>
6.编写 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>
<!-- 配置 mybatis 的环境 -->
<environments default="mysql">
<!-- 配置 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://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 告知 mybatis 映射配置的位置 -->
<mappers>
<mapper resource="com/ajth/cn/dao/IUserDao.xml"/>
</mappers>
</configuration>
7.编写测试类
public static void main(String[] args) throws IOException {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("src/SqlMapConfig.xml");
//2.创建 SqlSessionFactory 的构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = builder.build(in);
//4.使用 SqlSessionFactory 生产 SqlSession 对象
SqlSession session = factory.openSession();
//5.使用 SqlSession 创建 dao 接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
//6.使用代理对象执行查询所有方法
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
//7.释放资源
session.close();
in.close();
}
8.结果展示
log4j:WARN No appenders could be found for logger (org.apache.ibatis.logging.LogFactory).
log4j:WARN Please initialize the log4j system properly.
User{id=41, username='老王', birthday=Tue Feb 27 17:47:08 CST 2018, sex=男, address='北京'}
User{id=42, username='小二王', birthday=Fri Mar 02 15:09:37 CST 2018, sex=女, address='北京金燕龙'}
User{id=43, username='小二王', birthday=Sun Mar 04 11:34:34 CST 2018, sex=女, address='北京金燕龙'}
User{id=45, username='传智播客', birthday=Sun Mar 04 12:04:06 CST 2018, sex=男, address='北京金燕龙'}
User{id=46, username='老王', birthday=Wed Mar 07 17:37:26 CST 2018, sex=男, address='北京'}
User{id=48, username='小马宝莉', birthday=Thu Mar 08 11:44:00 CST 2018, sex=女, address='北京修正'}
User{id=49, username='华为', birthday=null, sex=男, address='北京市顺义区'}
User{id=52, username='小米', birthday=Mon Aug 10 09:27:23 CST 2020, sex=男, address='北京市顺义区'}
Process finished with exit code 0
1.4、Mybatis快速入门总结:
通过1.3的快速入门,发现mybatis使用是一件非常容易的事情。因为只需要编写dao接口并按照mybatis要求编写两个配置文件,就可以实现功能了。比使用JDBC方便多了。
但是这里面包含了许多细节,例如为什么会有工厂对象(SqlSessionFactory),为什么有了工厂之后还有构建者对象(SqlSessionFactoryBuilder),为什么IUserDao.xml在创建时有位置和文件名的要求等。
1.5、基于注解的Mybatis:
- 在持久层接口中添加注解
//mybatis注解方式
@Select("select * from user")
User getAll();
- 修改 SqlMapConfig.xml
<!-- 告知 mybatis 映射配置的位置 -->
<mappers>
<mapper class="com.ajth.cn.dao.IUserDao"/>
</mappers>
3.注意事项:
在使用基于注解的 Mybatis 配置时,请移除 xml 的映射配置(IUserDao.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>
<!-- 配置 mybatis 的环境 -->
<environments default="mysql">
<!-- 配置 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://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 告知 mybatis 映射配置的位置 -->
<!-- <mappers>
<mapper resource="com/ajth/cn/dao/IUserDao.xml"/>
</mappers>-->
<!--如果是用注解来配置的话,此处应该使用dao全限定类名-->
<mappers>
<mapper class="com.ajth.cn.dao.IUserDao"/>
</mappers>
</configuration>
1.6、基于代理 Dao 实现 CRUD 操作
注意事项(
1.dao接口和dao接口的映射文件必须在相同包下。
2.接口映射文件中 namespace标签的值必须写对应的dao接口的全限定类型。
3.SQL 语句的配置标签,,,的 id 属性必须和持久层接口的
方法名相同。
)
1.创建一个maven项目
2.导入所需依赖
<?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>com.ajth.cn</groupId>
<artifactId>mybatis_day02</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 导入mybatis依赖-->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
3.编写dao接口
package com.ajth.cn.dao;
import com.ajth.cn.pojo.User;
import org.apache.ibatis.annotations.Param;
/**
* @author xfd
* @version 1.0
* @date 2021/3/29 0029 22:44
*/
public interface IUserDao {
//保存用户
boolean saveUser(User user);
//删除一条用户记录
boolean deleteUserById(Integer id);
//根据id修改某条用户
boolean updateById(User user);
//根据id查询一条用户记录
User findById(Integer id);
}
4.编写映射文件
<?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.ajth.cn.dao.IUserDao">
<!--
parameterType:
入参类型,和接口中方法的入参类型一致。
resultType:
返回类型,和接口中方法的返回值类型一致。
#{}:
占位符相当于JDBC中的?,可以防止SQL注入。如果数据类型是基本数据类型
可以随意写。
#{}:内容写法
由于保存方法的入参是一个User类型的对象,所以#{}中要写user对象中的属性名称。
ognl 表达式:
它是 apache 提供的一种表达式语言,全称是:
Object Graphic Navigation Language 对象图导航语言
它是按照一定的语法格式来获取数据的。
语法格式就是使用 #{对象.对象}的方式
-->
<!--保存用户-->
<insert id="saveUser" parameterType="com.ajth.cn.pojo.User" >
INSERT INTO USER ( username, birthday, sex, address )
VALUE
(#{username},#{birthday},#{sex},#{address})
</insert>
<!--根据id删除一条记录-->
<delete id="deleteUserById" parameterType="Integer">
DELETE
FROM
`user`
WHERE
id = #{ssss}
</delete>
<!--根据id修改一条记录-->
<update id="updateById" parameterType="com.ajth.cn.pojo.User">
UPDATE USER
SET username =#{username},
birthday =#{birthday},
sex =#{sex},
address=#{address}
WHERE id=#{id}
</update>
<!-- 根据id查询 -->
<select id="findById" parameterType="Integer" resultType="com.ajth.cn.pojo.User">
SELECT
*
FROM
USER
WHERE
id = #{id}
</select>
</mapper>
5.编写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>
<!-- 配置 mybatis 的环境 -->
<environments default="mysql">
<!-- 配置 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://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 告知 mybatis 映射配置的位置 -->
<mappers>
<mapper resource="com/ajth/cn/dao/mapp/IUserDao.xml"/>
</mappers>
</configuration>
6.编写测试类
package com.ajth.cn.test;
import com.ajth.cn.dao.IUserDao;
import com.ajth.cn.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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
/**
* @author xfd
* @version 1.0
* @date 2021/3/29 0029 22:49
*/
public class UserTest {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession session;
private IUserDao userDao;
//在测试方法执行之前执行
@Before
public void init() throws Exception {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.创建 SqlSession 工厂对象
factory = builder.build(in);
//4.创建 SqlSession 对象
session = factory.openSession();
//5.创建 Dao 的代理对象
userDao = session.getMapper(IUserDao.class);
}
@Test
public void testFindOne() {
User user = userDao.findById(41);
System.out.println(user);
}
@Test
public void testSaveUser(){
User user = new User();
user.setUsername("王艳飞");
user.setBirthday(new Date());
user.setSex('男');
user.setAddress("河北省邢台市");
boolean flage = userDao.saveUser(user);
if (flage){
System.out.println("保存成功");
}else{
System.out.println("保存失败");
}
}
@Test
public void testDeleteUser(){
User byId = userDao.findById(53);
boolean flag = userDao.deleteUserById(byId.getId());
if(flag){
System.out.println("删除成功");
}else {
System.out.println("删除失败");
}
}
@Test
public void testUpdateById(){
User user = new User();
user.setId(41);
user.setUsername("张宝宝");
user.setBirthday(new Date());
user.setSex('女');
user.setAddress("湖北武汉");
boolean flage = userDao.updateById(user);
if(flage){
System.out.println("修改成功");
}else{
System.out.println("修改失败");
}
}
//在测试方法执行完成之后执行
@After
public void destroy() throws Exception {
session.commit();
//7.释放资源
session.close();
in.close();
}
}
1.7、Mybatis 与 JDBC 编程对比
1.数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
解决:
在 SqlMapConfig.xml 中配置数据链接池,使用连接池管理数据库链接。
2.Sql 语句写在代码中造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变 java 代码。
解决:
将 Sql 语句配置在 XXXXmapper.xml 文件中与 java 代码分离。
3.向 sql 语句传参数麻烦,因为 sql 语句的 where 条件不一定,可能多也可能少,占位符需要和参数对应。
解决:
Mybatis 自动将 java 对象映射至 sql 语句,通过 statement 中的 parameterType 定义输入参数的
类型。
4.对结果集解析麻烦,sql 变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo 对
象解析比较方便。
解决:
Mybatis 自动将 sql 执行结果映射至 java 对象,通过 statement 中的 resultType 定义输出结果的
类型。
1.8、Mybatis参数深入了解
parameterType 配置参数:
该属性的值可以是基本数据类型和String,也可以是实体类对象,还可以是实体类包装类对象。
基本数据类型和String可以直接写类名称,也可以写包名.类名的方式例如:
java.lang.String
实体类对象可以写权限定类名,也可以写别名假如你配置了别名)。
为什么基本数据类型和String就可以直接写类名,而实体类对象如果不配置别名就不可以使用呢?因为Mybatis在加载的时候已经把常用的基本数据类型和String注册了别名,所以可以使用。
一下是来自Mybatis官网
1.基本数据类型演示
<!--根据id删除一条记录-->
<delete id="deleteUserById" parameterType="Integer">
DELETE
FROM
`user`
WHERE
id = #{ssss}
</delete>
<!-- 根据id查询 -->
<select id="findById" parameterType="Integer" resultType="com.ajth.cn.pojo.User">
SELECT
*
FROM
USER
WHERE
id = #{id}
</select>
2.实体类对象类型演示
<!--保存用户-->
<insert id="saveUser" parameterType="com.ajth.cn.pojo.User" >
INSERT INTO USER ( username, birthday, sex, address )
VALUE
(#{username},#{birthday},#{sex},#{address})
</insert>
3.包装类对象演示
<!-- 根据QueryVO中条件查询用户信息 -->
<select id="findUserNameByQueryVo" resultType="com.ajth.cn.pojo.User" parameterType="com.ajth.cn.pojo.QueryVO">
SELECT * FROM USER WHERE username LIKE '%${user.username}%'
</select>
resultType 配置结果类型
1.基本数据类型演示
<!-- 查询总记录条数 -->
<select id="countUser" resultType="int">
SELECT COUNT(*) from USER
</select>
2.实体类对象类型演示
<!--根据id修改一条记录-->
<update id="updateById" parameterType="com.ajth.cn.pojo.User">
UPDATE USER
SET username =#{username},
birthday =#{birthday},
sex =#{sex},
address=#{address}
WHERE id=#{id}
</update>
1.9动态sql
<!--
if动态sql演示:
注意test中写的是实体类对象中的属性属性,如果是包装类使用OGNL表达式写法。
-->
<select id="findUserByUserNameAndAddressIf" resultMap="userMap" parameterType="user">
<include refid="userCommon"></include> WHERE 1=1
<if test="userName != null and userName != '' ">
AND username LIKE #{userName}
</if>
<if test="userAddress != null and userAddress != '' ">
AND address LIKE #{userAddress}
</if>
</select>
<!--动态sql之where演示-->
<select id="findUserByUserNameAndAddressWhere" resultMap="userMap" parameterType="user">
<include refid="userCommon"></include>
<where>
<if test="userName != null and userName != '' ">
AND username LIKE #{userName}
</if>
<if test="userAddress != null and userAddress != '' ">
AND address LIKE #{userAddress}
</if>
</where>
</select>
<!-- 动态sql之foreach演示-->
<select id="findUserByUserNameAndAddressForeach" resultMap="userMap" parameterType="queryVo">
<include refid="userCommon"></include>
<where>
<if test="ids != null and ids.size() > 0 ">
<foreach collection="ids" open="id in ( " close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>