MyBatis是什么?
MyBatis是一个支持普通SQL查询、存储过程以及高级映射的持久层框架,消除了几乎所有的JDBC代码和参数的手动设置以及对结果集的检索,并使用简单的XML或者注解进行配置和原始映射。用以将接口和Java的POJO映射成数据库中的记录,使得java开发人员可以使用面向对象的思想来操作数据库。MyBatis通过描述Java对象和数据库表之间的映射关系,自动化将java应用程序中的对象持久化到关系型数据库的表中。MyBatis框架的工作原理如下图所示:
从上图中可以看到,在使用MyBatis框加之后,应用程序不再直接访问底层数据库了,而是以面向对象的方式来访问底层的数据库,MyBatis框架会通过映射的关系将面向对象的操作转换为底层的SQL操作,MyBatis需要手动匹配提供POJO、SQL和映射关系。
MyBatis工作原理
- 读取mybatis配置文件。mybatis配置配置文件作为MyBatis的全局配置文件,配置了MyBatis运行时环境等信息,主要内容是获取数据库连接。
- 加载映射文件Mapper.xml。Mapper.xml文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在mybatis-config.xml中。
- 构建会话工厂SqlSessionFactory。
- 创建对象SqlSession。sqlsession对象中包含了执行SQL的所有方法。
- MyBatis底层定义了一个Executor接口来操作数据库,会根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时查询缓存的维护。
- 在Executor接口的执行方法中,包含一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等。Mapper.xml中的一个SQL对一个MappedStatement对象,SQL的id即MappedStatement的id。
- 输入参数映射。在执行方法的时,MappedStatement对象会用户执行SQL语句的输入参数进行定义,Executor执行器会通过MappedStatement对象在执行SQL语句之前,将输入的Java对象映射到SQL语句中。
- 输出结果映射。在数据库中执行完SQL语句之后,MappedStatement对象会对SQL执行输出的结果进行定义,Executor执行器会通过MappedStatement对象在执行SQL语句之后,将输出结果映射到Java对象中。
MyBatis增删改查入门
根据用户编号,查询用户信息。
首先在mysql数据库中创建一个mybatis的数据库,然后创建一个user表,同时插入几条数据。
创建mybatis数据库
create database mybatis;
使用mybatis数据库
use mybatis;
# 在mybatis数据库中创建user表
create table user(
id int primary key AUTO_INCREMENT,
name varchar(20) not null,
age int not null,
salary double not null
)
#插入数据
insert into user(name,age,salary) values('积木',23,23.56),('刘德华',36,36.65)
#查询有没有插入成功
select * from user;
打开IDEA,创建一个名字为mybatis_test_01的工程,然后在Pom.xml文件中加入mybatis,jdbc,junit以及log4j的坐标:
<?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.mybatis</groupId>
<artifactId>mybatis_test_01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</project>
在resources文件目录下面创建mybatis全局配置文件mybatis-config.xml,用于配置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>
<!--使用properties标签引入数据库配置-->
<properties resource="jdbcConfig.properties"></properties>
<!--使用typeAliases标签批量扫描指定包下面的类,别名为类名-->
<typeAliases>
<package name="com.mybatis.domain"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--使用package注册指定包下面的所有接口-->
<package name="com.mybatis.dao"/>
</mappers>
</configuration>
在resources目录下面定义jdbcConfig.properties文件,配置数据库连接信息
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=xxxxxx,
导完坐标之后在src目录下面新增com.mybatis.domain包,在该包下创建持久化类User,并在类中声明id、name、age、salay属性,以及对应的getter/setter以及toString方法,User就是一个POJO(一个普通的Java对象),MyBatis就是采用持久化类来完成数据库操作的。
package com.mybatis.domain;
import java.io.Serializable;
public class User implements Serializable {
private Integer id;
private String name;
private Double salary;
public Integer getId() {
return id;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
'}';
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
}
在src目录下面创建一个com.mybatis.dao包,创建持久层接口IUserDao类,通过id主键精确查找用户信息。
package com.mybatis.dao;
import com.mybatis.domain.User;
public interface IUserDao {
//根据id查找用户
User findUserById(Integer id);
}
接下来就是在持久层相同的包中创建持久层接口映射文件IUserDao.xml。这里注意在mapper.xml文件中,最好使用#{}的形式,调用PreparedStatement以防止SQL注入,关于SQL注入可以看我这篇博客《mybatis入门实战&源码解读番外篇(1):mybatis中#{}和${}的区别以及SQL注入问题分析》
<?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.mybatis.dao.IUserDao">
<select id="findUserById" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
</mapper>
上面的2、3、4行是mybatis的约束配置,5-7行是我们编写的信息,是配置文件的根元素,包含了一个namespace属性,这个属性为这个指定了唯一的命名空间,通常会设置成"包名+SQL映射文件名"的形式。子元素中的信息是用于执行查询操作的配置,其id属性是元素在映射文件中的唯一标识;parameterType属性用于指定传入参数的类型,这里表示的是传递给SQL的是一个Integer类型的参数;ResultType属性用于返回结果的类型,这里表示返回的数据是User类型;#{}用于表示一个占位符,#{id}表示该占位符接收的参数名称为id。接下来在test目录下面创建一个com.mybatis.test的包,在该包中创建UserTest的类,并在类中编写如下测试方法:testFindUserById
package com.mybatis.test;
import com.mybatis.dao.UserDao;
import com.mybatis.dao.UserDao1;
import com.mybatis.domain.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.IOException;
import java.io.InputStream;
import java.util.Date;
public class UserTest {
private InputStream inputStream;
private SqlSessionFactory sqlSessionFactory;
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder;
private SqlSession sqlSession;
private UserDao userDao;
@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
sqlSession = sqlSessionFactory.openSession();
userDao = sqlSession.getMapper(UserDao.class);
}
@After
public void close() throws IOException {
sqlSession.commit();
sqlSession.close();
inputStream.close();
}
//同一个会话,相同的参数,会调用一级缓存
@Test
public void testFindUserById(){
User user1 = userDao.findUserById(3);
User user2 = userDao.findUserById(3);
System.out.println(user1);
System.out.println(user2);
System.out.println(user1==user2);//true
}
}
查找结果如下:
User{id=1, name='积木', salary=23.56}
查找所有用户
查找所有用户的实现非常简单,只需要在映射文件中通过元素中编写相应的SQL语句,并通过SqlSession的查询方法执行该SQL语句即可。具体的实现步骤如下:
(1)在IUserDao类中添加findAllUsers方法,用于查找出所有的用户信息。
package com.mybatis.dao;
import com.mybatis.domain.User;
import java.util.List;
public interface IUserDao {
//查找所有用户
List<User> findAllUsers();
}
(2)在映射文件IUserDao.xml中,添加查找所有用户信息的SQL语句。具体的实现代码如下:
<?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.mybatis.dao.IUserDao">
<select id="findAllUsers" resultType="user" useCache="true" >
select * from user;
</select>
</mapper>
(3)在测试类中编写测试方法testFindAllUsers,具体实现如下:
package com.mybatis.test;
import com.mybatis.dao.IUserDao;
import com.mybatis.domain.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.IOException;
import java.io.InputStream;
import java.util.List;
public class UserTest {
private InputStream inputStream;
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder;
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
private IUserDao iUserDao;
@Before
public void init() throws IOException {
inputStream=Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
sqlSession = sqlSessionFactory.openSession();
iUserDao=sqlSession.getMapper(IUserDao.class);
}
@After
public void destory() throws IOException {
sqlSession.commit();
sqlSession.close();
inputStream.close();
}
//查找所有用户
@Test
public void testFindAllUsers(){
List<User> allUsers = iUserDao.findAllUsers();
for (User user:allUsers){
System.out.println(user);
}
}
}
根据用户名模糊查找用户
(1)在IUserDao类中添加findAllUsers方法,用于模糊查找出所有的用户信息。
package com.mybatis.dao;
import com.mybatis.domain.User;
import java.util.List;
public interface IUserDao {
//根据用户姓名模糊查找用户
List<User> findUserByName(String name);
}
(2)在映射文件IUserDao.xml中,添加查找所有用户信息的SQL语句。具体的实现代码如下:
<?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.mybatis.dao.IUserDao">
<select id="findUserByName" resultType="user" useCache="true" parameterType="java.lang.String">
select * from user where name like '%${name}%'
</select>
</mapper>
(3)在测试类中编写测试方法testFindUserByName,具体实现如下:
package com.mybatis.test;
import com.mybatis.dao.IUserDao;
import com.mybatis.domain.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.IOException;
import java.io.InputStream;
import java.util.List;
public class UserTest {
private InputStream inputStream;
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder;
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
private IUserDao iUserDao;
@Before
public void init() throws IOException {
inputStream=Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
sqlSession = sqlSessionFactory.openSession();
iUserDao=sqlSession.getMapper(IUserDao.class);
}
@After
public void destory() throws IOException {
sqlSession.commit();
sqlSession.close();
inputStream.close();
}
//通过姓名模糊查找用户
@Test
public void testFindUserByName(){
sqlSession.clearCache();
List<User> users = iUserDao.findUserByName("' or '1'='1 #name=xxx and password=xxx");
for (User user:users){
System.out.println(user);
}
}
}
新增用户、删除用户以及修改用户操作
(1)在IUserDao类中添加addUser方法用于新增用户,updateUser方法用于更新用户,deleteUser方法用于删除用户。
package com.mybatis.dao;
import com.mybatis.domain.User;
import java.util.List;
public interface IUserDao {
//新增用户
int addUser(User user);
//修改用户
int updateUser(User user);
//删除用户
int deleteUser(Integer id);
}
(2)在映射文件IUserDao.xml中,添加增、删、改用户信息的SQL语句。具体的实现代码如下:
<?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.mybatis.dao.IUserDao">
<!--新增用户-->
<insert id="addUser" parameterType="user" >
<selectKey keyProperty="id" keyColumn="id" resultType="int">
select last_insert_id();
</selectKey>
insert into user(name,age,salary,address) values(#{name},#{age},#{salary},#{address})
</insert>
<!--删除用户-->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id=#{id}
</delete>
<!--修改用户-->
<update id="updateUser" parameterType="user">
update user set name=#{name},age=#{age},salary=#{salary},address=#{address} where id=#{id}
</update>
</mapper>
(3)在测试类中编写测试方法testAddUser、testUpdateUser、testDeleteUser,具体实现如下:
package com.mybatis.test;
import com.mybatis.dao.IUserDao;
import com.mybatis.domain.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.IOException;
import java.io.InputStream;
import java.util.List;
public class UserTest {
private InputStream inputStream;
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder;
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
private IUserDao iUserDao;
@Before
public void init() throws IOException {
inputStream=Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
sqlSession = sqlSessionFactory.openSession();
iUserDao=sqlSession.getMapper(IUserDao.class);
}
@After
public void destory() throws IOException {
sqlSession.commit();
sqlSession.close();
inputStream.close();
}
//新增用户
@Test
public void testAddUser(){
User user = new User();
user.setName("张瑞敏");
user.setSalary(123.321);
user.setAge(12);
user.setAddress("浙江嘉兴");
int i = iUserDao.addUser(user);
System.out.println(i);
}
//修改用户
@Test
public void testUpdateUser(){
User user = new User();
user.setId(2);
user.setName("马化腾");
user.setSalary(123.321);
user.setAge(23);
user.setAddress("深圳市");
iUserDao.updateUser(user);
}
//删除用户
@Test
public void testDeleteUser(){
int i = iUserDao.deleteUser(2);
System.out.print(i);
}
}