前言
Mybatis
是一款Java
持久层框架,内部将操作数据库需要的Jdbc
相关代码进行了封装,同时能将SQL
语句执行结果与Pojo
直接进行映射。本篇文章将先对Jdbc
进行学习,并在此基础上学习Mybatis
的基础使用,无论是Jdbc
还是Mybatis
,均是基于原生组件,不会与Spring
等框架进行整合。
Mybatis
版本:3.5.6
正文
一. Jdbc基础
Jdbc
全称为JavaDataBase Connectivity
,即Java
数据库连接,基于Jdbc
可以获取数据库连接并执行SQL
语句,还能处理SQL
语句的执行结果集。下面将对Jdbc
的操作步骤进行介绍。
1. 加载数据库驱动
首先需要加载数据库驱动,这里以Mysql
数据库驱动为例,如下所示。
Class.forName("com.mysql.jdbc.Driver");
2. 获取数据库连接
然后需要获取数据库连接,如下所示。
// url用于标识数据库位置,即告诉Jdbc连接哪个数据库
String url = "jdbc:mysql://127.0.0.1:3306/test";
// 数据库用户名和密码
String username = "root";
String password = "root";
// Connection是数据库编程中的一个重要对象,客户端与数据库的所有交互均依赖该对象
Connection connection = DriverManager.getConnection(url, username, password);
Connection
对象部分重要方法如下所示。
方法 | 描述 |
---|---|
Statement createStatement() | 创建向数据库发送SQL 的Statement 对象 |
PreparedStatement prepareStatement(String sql) | 创建向数据库发送预编译SQL 的PreparedStatement 对象 |
commit() | 提交事务 |
rollback() | 回滚事务 |
3. 获取Statement对象
Statement
对象用于向数据库发送SQL
语句,获取方式如下所示。
Statement statement = connection.createStatement();
Statement
对象的常用方法如下所示。
方法 | 描述 |
---|---|
ResultSet executeQuery(String sql) | 向数据库发送查询SQL 语句 |
int executeUpdate(String sql) | 向数据库发送插入,更新或删除SQL 语句 |
boolean execute(String sql) | 向数据库发送任意SQL 语句 |
使用Statement
的查询示例如下所示。
Statement statement = connection.createStatement();
String sql = "SELECT * FROM worker";
ResultSet resultSet = statement.executeQuery(sql);
由于Statement
的使用会引入安全和效率问题,所以通常Jdbc
执行SQL
语句是基于PreparedStatement
对象。PreparedStatement
继承于Statement
,使用PreparedStatement
的查询示例如下所示。
String sql = "SELECT * FROM worker WHERE sex = ?"
// 获取PreparedStatement对象时就需要传入SQL语句进行预编译
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 设置参数,将第一个占位符替换为"male"
preparedStatement.setString(1, "male");
// 执行查询,查询结果由ResultSet对象封装
ResultSet resultSet = preparedStatement.executeQuery();
相较于Statement
对象,PreparedStatement
对象执行效率更高(因为对SQL
语句完成了预编译),并且可以防止SQL
注入(因为获取PreparedStatement
对象时就需要传入SQL
语句进行预编译,此时不会产生用户输入数据改变SQL
语句结构的现象)。
4. 处理执行结果
ResultSet
代表Jdbc
中SQL
语句的执行结果。ResultSet
封装执行结果时,采用的是表格的方式,ResultSet
对象维护了一个指向表格数据行的游标,初始状态时,游标在第0行(此时没有指向结果数据),调用next()
方法后,游标会指向结果数据里的第一行数据,此时可以通过ResultSet
对象来操作游标指向的这一行数据。ResultSet
的常用方法如下所示。
方法 | 描述 |
---|---|
boolean next() | 游标移动到下一行 |
boolean previous() | 游标移动到上一行 |
boolean absolute( int row ) | 游标移动到指定行 |
Object getObject(int columnIndex) | 获取游标指向的行的第columnIndex列的任意类型数据 |
Object getObject(String columnLabel) | 获取游标指向的行的columnLabel列的任意类型数据 |
5. 释放连接
操作完数据库后,需要释放连接,如下所示。
if (resultSet != null) {
resultSet.close();
}
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
二. Mybatis基础使用
1. Mybatis结构
Mybatis
的一个整体结构如下所示。
上图中出现的Mybatis
的组件的说明如下。
组件 | 说明 |
---|---|
mybatis-config.xml | Mybatis 的全局配置文件。配置Mybatis 的运行环境(数据库连接等)。 |
Mapper.xml | 映射文件。映射文件中配置了操作数据库的SQL 语句。 |
SqlSessionFactory | 通过mybatis-config.xml 文件构建SqlSessionFactory ,称为会话工厂。用于创建SqlSession 。 |
SqlSession | 通过SqlSessionFactory 创建SqlSession ,称为会话。操作数据库需要通过SqlSession 进行。 |
Executor | SqlSession 内部通过Executor 执行数据库操作。 |
MappedStatement | Executor 通过MappedStatement 在执行SQL 语句前将输入的参数映射到SQL 语句中,在执行SQL 语句后将输出结果映射到设置的输出结果类型中。 |
2. 原始Mybatis使用
原始Mybatis
的使用中,在引入Mybatis
的包以及配置好mybatis-config.xml
之后,最重要的就是编写映射文件(Mapper.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="MyMapper">
<select id="selectWorker" parameterType="java.lang.String" resultType="com.test.po.Worker">
SELECT * FROM worker WHERE sex = #{sex}
</select>
</mapper>
原始Mybatis
执行映射文件时,首先需要获取SqlSession
,然后通过SqlSession
来调用映射文件。获取SqlSession
的步骤如下所示。
// 指定全局配置文件
String resource = "mybatis-config.xml";
// 读取全局配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
// 构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession
调用映射文件的示例如下所示。
// 第一个参数为:(映射文件的)命名空间 + "." + (映射文件的SQL语句的)id
// 第二个参数为:需要执行的SQL语句的参数值
List<Worker> workers = sqlSession.selectList("MyMapper.selectWorker", "male");
最后不要忘记关闭SqlSession
,如下所示。
if (sqlSession != null) {
sqlSession.close();
}
3. 动态代理Mybatis使用
动态代理Mybatis
使用时,需要编写映射文件和映射接口,这里先给出映射文件,如下所示。
<mapper namespace="com.test.mapper.WorkerMapper">
<select id="selectWorkerBySex" parameterType="java.lang.String" resultType="com.test.po.Worker">
SELECT * FROM worker WHERE sex = #{sex}
</select>
</mapper>
上面的映射文件中,<mapper>
标签的namespace属性需要设置为与映射文件关联的映射接口的全限定名,<select>
标签的id属性需要与映射接口定义的方法名一致,即映射接口定义的每一个方法需要和映射文件中的某一条SQL
相对应,Mybatis
会为映射接口生成映射实例,通过调用映射实例的方法来完成对数据库的操作。上面的映射文件关联的映射接口如下所示。
public interface WorkerMapper {
List<Worker> selectWorkerBySex(String sex);
}
动态代理Mybatis
执行时,首先还是需要获取SqlSession
,然后通过SqlSession
来获取映射接口的实例。获取SqlSession
的步骤如下所示。
// 指定全局配置文件
String resource = "mybatis-config.xml";
// 读取全局配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
// 构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
获取映射接口的实例的步骤如下所示。
WorkerMapper workerMapper = sqlSession.getMapper(WorkerMapper.class);
获取映射接口的实例后,通过调用映射接口的实例的方法来执行SQL
语句,如下所示。
List<Worker> list = workerMapper.selectWorkerBySex("male");
最后关闭SqlSession
,如下所示。
if (sqlSession != null) {
sqlSession.close();
}
三. Mybatis标签语法
本节对映射文件中的常用标签进行分析,无特殊说明时,默认Mybatis
的使用是基于动态代理的方式。
1. <select>标签
<select>
标签表示查询SQL
语句,标签常用属性及说明如下表所示。
属性 | 说明 |
---|---|
id | 当前namespace下的唯一标识,要求id的值与映射接口中的方法名称一致。 |
parameterType | 传入SQL 语句的参数的类型的全限定名(可以省略,Mybatis 可以自己推断出传入参数的类型)。 |
resultType | SQL 语句执行后返回的数据的类型的全限定名,如果返回的是集合类型,则resultType为集合存储的元素的类型的全限定名。 |
resultMap | 与<resultMap> 标签配合使用。 |
如果需要传入多个参数,可以通过传入Map
或者Java Bean
来实现。下面将举两个例子来说明<select>
标签的使用。
例子1:通过Map
传入两个参数,分别为age和salary,然后查询年龄大于age并且工资大于salary的所有工人数据。
映射文件如下所示。
<?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.test.mapper.WorkerMapper">
<select id="selectAgeSalaryByMap" parameterType="java.util.Map"
resultType="com.test.po.Worker">
SELECT * FROM worker WHERE age > #{age} and salary > #{salary}
</select>
</mapper>
映射接口如下所示。
public interface WorkerMapper {
List<Worker> selectAgeSalaryByMap(Map<String, Long> map);
}
测试代码如下所示(省略SqlSession
的获取)。
......
WorkerMapper workerMapper = sqlSession.getMapper(WorkerMapper.class);
Map<String, Long> map = new HashMap<>();
map.put("age", 20L);
map.put("salary", 10000L);
List<Worker> workers = workerMapper.selectAgeSalaryByMap(map);
......
例子2:通过Java Bean
传入两个参数,分别为age和salary,然后查询年龄大于age并且工资大于salary的所有工人数据。
Java Bean
如下所示。
public class SelectInfo {
private Long age;
private Long salary;
public Long getAge() {
return age;
}
public void setAge(Long age) {
this.age = age;
}
public Long getSalary() {
return salary;
}
public void setSalary(Long salary) {
this.salary = salary;
}
}
映射文件如下所示。
<?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.test.mapper.WorkerMapper">
<select id="selectAgeSalaryByBean" parameterType="com.test.po.SelectInfo"
resultType="com.test.po.Worker">
SELECT * FROM worker WHERE age > #{age} and salary > #{salary}
</select>
</mapper>
映射接口如下所示。
public interface WorkerMapper {
List<Worker> selectAgeSalaryByBean(SelectInfo selectInfo);
}
测试代码如下所示(省略SqlSession
的获取)。
......
WorkerMapper workerMapper = sqlSession.getMapper(WorkerMapper.class);
SelectInfo selectInfo = new SelectInfo();
selectInfo.setAge(20L);
selectInfo.setSalary(10000L);
List<Worker> workers = workerMapper.selectAgeSalaryByBean(selectInfo);
......
2. <insert>标签
<insert>
标签表示插入SQL
语句,Mybatis
执行完一条插入语句后,将返回一个整数表示其影响的行数。<insert>
标签属性与<select>
标签属性大部分相同,下表给出不同的部分。
属性 | 说明 |
---|---|
keyProperty | 设置为主键对应的属性时,可以获取插入操作后插入数据的主键值。 |
useGeneratedKeys | 该属性为true时,Mybatis 会使用JDBC 的getGeneratedKeys() 方法获取数据库内部产生的主键。 |
如下给出一个插入数据后获取插入数据的自增主键的示例。映射文件如下所示。
<?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.test.mapper.WorkerMapper">
<insert id="insertWorker" parameterType="com.test.po.Worker"
keyProperty="id" useGeneratedKeys="true">
INSERT INTO
worker (name, sex, age, salary)
VALUES
(#{name}, #{sex}, #{age}, #{salary})
</insert>
</mapper>
映射接口如下所示。
public interface WorkerMapper {
Integer insertWorker(Worker worker);
}
Worker
类结构如下所示。
public class Worker {
private long id;
private String name;
private String sex;
private long age;
private long salary;
public Worker() {}
public Worker(String name, String sex,
long age, long salary) {
this.name = name;
this.sex = sex;
this.age = age;
this.salary = salary;
}
// 省略getter和setter
......
}
worker表的创建语句如下。
CREATE TABLE worker(
id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
name VARCHAR(30) NOT NULL,
sex VARCHAR(30) NOT NULL,
age INT(11) NOT NULL,
salary INT(11) NOT NULL
)
测试代码如下所示(省略SqlSession
的获取)。
...
WorkerMapper workerMapper = sqlSession.getMapper(WorkerMapper.class);
Worker worker = new Worker("Lee", "male", 20L, 10000L);
Integer result = workerMapper.insertWorker(worker);
System.out.println(result + " pieces of data were inserted.");
System.out.println("The primary key of the inserted data is " + worker.getId());
...
3. <update>和<delete>标签
这两个标签的属性与<select>
和<insert>
标签的属性大体一致,并且在执行后会返回一个整数表示影响的记录数。
4. <resultMap>标签
从数据库中查询出数据时,通常会将查询出来的数据映射成一个Java Bean
,但是如果数据库表的列名与Java Bean
的属性名不一致时,会无法进行映射,此时需要使用<resultMap>
标签来指定表列名和Java Bean
属性名之间的映射关系。现在创建一张名为book的表,如下所示。
CREATE TABLE book(
id INT(11) PRIMARY KEY AUTO_INCREMENT,
b_name VARCHAR(255) NOT NULL,
b_price INT(11) NOT NULL
)
创建一个Book
类,用于与book表的查询结果进行映射,如下所示。
public class Book {
private long id;
private String bookName;
private long bookPrice;
// 省略getter和setter
......
}
可以看到,Book
类的bookName和bookPrice属性与book表的b_name和b_price列的名称不一致,从book表查询出的数据无法映射到Book
类上,此时需要使用<resultMap>
标签来解决无法映射的问题。映射文件如下所示。
<?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.test.mapper.BookMapper">
<resultMap id="bookResultMap" type="com.test.po.Book">
<result column="id" property="id"/>
<result column="b_name" property="bookName"/>
<result column="b_price" property="bookPrice"/>
</resultMap>
<select id="selectBookByName" parameterType="java.lang.String"
resultMap="bookResultMap">
SELECT * FROM book WHERE b_name=#{bookName}
</select>
</mapper>
<resultMap>
标签的id属性表示当前namespace下的唯一标识,可以通过<resultMap>
标签的id属性来引用该<resultMap>
。<resultMap>
标签的type属性表映射类的全限定名,比如需要与Book
类进行映射,则type字段应该为Book
类的全限定名。<resultMap>
标签下,一个<result>
标签代表一对表列名和类属性名的映射,<result>
标签的column属性为表的列名,<result>
标签的property属性为类属性名。
上面映射文件对应的映射接口如下所示。
public interface BookMapper {
List<Book> selectBookByName(String bookName);
}
测试代码如下所示(省略SqlSession
的获取)。
......
BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
List<Book> mathBooks = bookMapper.selectBookByName("Math");
......
如果Book
类中有一个属性为一个类,如下所示。
Book
。
public class Book {
private long id;
private String bookName;
private long bookPrice;
private BookStore bookStore;
// 省略getter和setter
......
}
BookStore
。
public class BookStore {
private long bsId;
private String bsName;
// 省略getter和setter
......
}
此时映射文件如下所示。
<?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.test.mapper.BookMapper">
<resultMap id="bookResultMap" type="com.test.po.Book">
<result column="id" property="id"/>
<result column="b_name" property="bookName"/>
<result column="b_price" property="bookPrice"/>
<association property="bookStore" javaType="com.test.po.BookStore">
<result column="bs_id" property="bsId"/>
<result column="bs_name" property="bsName"/>
</association>
</resultMap>
<select id="selectBookByName" parameterType="java.lang.String"
resultMap="bookResultMap">
SELECT * FROM book WHERE b_name=#{bookName}
</select>
</mapper>
关于Mybatis
标签的基础语法就分析到这里,其中<resultMap>
标签作为Mybatis
中的功能最强大标签,其所能实现的功能远不止上述介绍的功能,后续会单独就<resultMap>
标签的使用进行深入学习。
总结
本文对JDBC
的基础使用和Mybatis
的基础使用进行了分析,并针对Mybatis
的映射文件中的常用标签的基础语法进行了介绍。通常Mybatis
的使用需要编写映射文件和映射接口,且一个映射文件与一个映射接口相对应,Mybatis
会使用JDK
动态代理为映射接口生成代理对象,当调用映射接口的方法时,调用请求会被代理对象拦截,然后根据映射接口全限定名+方法名定位到相应的MappedStatement
然后执行SQL
语句。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。