一、MyBatis简介
MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github。
MyBatis 是一个简化和实现了 Java 数据持久化层(persistence layer)的开源框架,它抽象了大量的 JDBC 冗余 代码,并提供了一个简单、易用的 API 和数据库交互。和SSH中的Hibernate有着同样的作用。
1、为什么使用mybatis框架
在我们之前的数据库操作种使用单纯的 Java 的 JDBC 编程仍然存在大量的问题,比如频繁的打开和关闭链接造成资源浪费、硬编码问题等。
⬇️使用传统JDBC操作数据
try
{
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8";
String user = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, user, password);
String sql = "select * from user where user_name=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "zhangsan");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
User us = new User();
us.setUserId(rs.getInt("user_id"));
us.setUsername(rs.getString("user_name"));
us.setPassword(rs.getString("user_pass"));
us.setCname(rs.getString("cname"));
}
conn.close();
} catch(
Exception e)
{
e.printStackTrace();
}
MyBatis框架可以让编程人员注意在SQL 语句的编写上,通过 MyBatis 提供的映射方式,自由灵活的创建出满足业务需求的各种 SQL 语句,不管是单表查询,还是多表查询,都 可以很好的完成。如果要执行的 SQL 语句中存在占位符,那么 JDBC 在执行之前,需要向执行该 SQL 语句的 PreparedStatement 对象传入参数。Mybatis 可以将参数进行自动的输入映射,查询之后的结果集,Mybatis 可以灵活的映射成 Java 对象,这就是MyBatis的输入映射和输出映射。
2、快速入门
可以进入mybatis的官网下载所需要的文件或者教程(https://mybatis.org/mybatis-3/zh/index.html)。
1、pom依赖
需要的依赖有mybatis、mysql-connector、junit、log4j
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<mybatis.version>3.5.6</mybatis.version>
<mysql.version>8.0.30</mysql.version>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.18.26</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<!--mybatis核心依赖-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!--mysql驱动-->
<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!--日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
log4j的内容
# 定义输出级别为 debug,输出名称为 stdout
log4j.rootLogger=debug,stdout
# 定义 stdout 的输出采用哪个类来执行
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# 定义 stdout 的输出类型的样式布局
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 定义 stdout 样式布局的消息格式
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
2、核心配置文件 mybatis-config.xml
配置文件采用dtd校验,DTD校验的限定为configuration(properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)
主要元素有
configuration元素:所有配置的根元素
environments元素:元素用于配置 MyBatis 的运行环境,内部可以有多个environment 对应不同的数据库(开发时、测试时、生产时),只需要更改
transactionManager 元素:用于配置事务管理。
dataSource 元素:用于配置数据源,type 指定数据源类型,值为 pooled 表示数据库连接。
mappers元素:内部有多个mapper,对应XXXmapper.xml文件中的数据操作,mapper中提供resource属性,表示xxxmaper.xml文件的位置,当然你也可以选择url和class两个属性来代替resource。但是当文件比较多的时候我们可以直接写“文件所在的文件夹”,即
<?xml version="1.0" encoding="GBK"?>
<!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.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value=""/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/bookMapper.xml"></mapper>
</mappers>
</configuration>
3、mapper.xml
mapper.xml文件的根标签为
mapper标签提供namespace属性用于指定命名空间以区别不同mapper.xml文件中的SQL,在这个入门案例中namespace的值可以自行定义,但是再高级Mapper代理中该值有其他重要作用。
与下面的select标签中的语句一样,mapper标签中有所有CRUD的标签,其中id对应方法名(这里的方法名你可以理解为本来应该由dao层实现的方法放到了mapper.xml配置文件中,在以后的Mapper高级代理中也可以这么认为),resultType为返回值类型(输出映射),一般写成全路径的包名,parameterType代表参数类型(输入映射)。
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="bookMapper">
<select id="findById" resultType="com.ls.model.BookTest">
select book_id,book_name bookName,book_author bookAuthor from book where book_id = #{bookId}
</select>
<select id="findAll" resultType="com.ls.model.BookTest">
select book_id bookId,book_name bookName,book_author bookAuthor from book
</select>
</mapper>
3、创建数据库连接数据源
数据表如下:
CREATE TABLE `book` (
`book_id` int NOT NULL AUTO_INCREMENT,
`book_name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
`book_author` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`book_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
在Idea中配置数据库连接,这个就不细讲了:
4、创建项目
项目的结构如下,这里我创建的是聚合工程,这个随自己的喜好。
其他文件在上文已经给出,下面给出主程序TestSqlSession和实体类BookTest的代码块。
/**
* date: 2023/2/23
*
* @author Arc
*/
package com.ls.model;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
@Data
@Accessors(chain = true)
public class BookTest {
private int book_id;
private String bookName;
private String bookAuthor;
private Date createTime;
private Date updateTime;
}
/**
* date: 2023/2/23
*
* @author Arc
*/
package com.ls;
import com.ls.model.BookTest;
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 TestSqlSession {
public static void main(String[] args) throws IOException {
InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
SqlSession sqlsession = factory.openSession();
// BookTest book = sqlsession.selectOne("bookMapper.findById", 2);
// System.out.println(book);
List<BookTest> books = sqlsession.selectList("bookMapper.findAll");
for (BookTest book1 : books) {
System.out.println(book1);
}
}
}
下面简单讲解一下程序的内容:
BookTest中使用了lombok插件所以省略了gettersetter,且启用了链式编程风格。
下面是TestSqlSession的内容,不论怎样的操作基本都是这几步。
//创建一个流来读取核心配置文件
InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
//根据配置文件创建了一个SQLSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
//通过工厂获得SqlSession对象,通过调用工厂的openSession方法
SqlSession sqlsession = factory.openSession();
需要注意factory.openSession();的时候尚未与数据库进行连接,可见下图我们将数据源的密码删除掉,程序并没有给出报错。
在mapper.xml文件中我们写一个select的“方法”findAll,并把这个mapper配置到mybatis-config.xml中,具体的SQL语句无异于JDBC所学的。
<select id="findAll" resultType="com.ls.model.BookTest">
select book_id bookId,book_name bookName,book_author bookAuthor from book
</select>
<mappers>
<mapper resource="mapper/bookMapper.xml"></mapper>
</mappers>
调用我们写的findAll“方法”,通过sqlsession对象来调用mapper.xml中的“方法”,可以看到sqlsession有很多已经写好的方法,我们使用selectList,注意在参数名之前加上namespace的前缀,来让程序区分不同的mapper。
List<BookTest> books = sqlsession.selectList("bookMapper.findAll");
for (BookTest book1 : books) {
System.out.println(book1);
}
}
使用一个List的对象来接受结果集,使用迭代器迭代打印出结果。
5、后话
关于事务的提交:在我们之前的JDBC中,事务会自动提交,但是在MyBatis中,凡是对数据修改(增删改)的操作,都需要将事务手动提交,即调用sqlsession.commit();\
selectList和selectOne:二者都是接收返回的结果,ctrl点击selectList后你就会发现selectone
也是基于selectList实现的,所以除非你能确定返回的是一条记录,否则不要轻易使用selectOne。
输出、输入映射:mybatis自动识别入参对象,但返回值类型不能不写。之前我们说resultType的value是返回值的类型,但是findAll返回的是一个List但是我们写的却是BookTest,这一点mybatis会自动识别,所以我们只需要写集合泛型内的类型即可。
在findById这一配置中,入参是一个Integer,我们并没有指明parameterType,但是在更复杂的数据类型,推荐使用注解将参数传入。