MyBatis简介
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
MyBatis 的主要作用就是在java中操作数据库,其实就是在jdbc的基础上进行了封装,使用mybatis之后,开发者不用再像使用jdbc那样去处理诸如注册驱动、创建Connection、配置Statement、执行sql语句、获取结果集等繁琐过程。早期叫做iBatis,后来改名为myBatis,在官网上有中文版的介绍和文档,mybatis同时支持xml和注解。
使用mybatis操作数据库
1.创建一个maven项目,pom.xml文件如下。
<?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.h3c</groupId>
<artifactId>learnmybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!---mybatis的依赖jar包->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--mysql的数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<!--用于在控制台打印出sql信息-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<!--若*Mapper.xml不放在src/main/resources目录下,需要添加如下配置,否则maven是
不会将该xml文件发布到编译后的classes目录下,这样就会导致mybatis到不到该文件。
-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
2.创建一个Student实体类并建立对应的数据库表:
package com.h3c.bean;
import org.apache.ibatis.type.Alias;
//@Alias("student")
public class Student {
private int id;
private String name;
private int age;
private double score;
//省略constructor、getter、setter、toString方法
}
CREATE TABLE `learnmybatis`.`t_student` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) NULL,
`age` INT NULL,
`score` DOUBLE NULL,
PRIMARY KEY (`id`));
3.定义一个dao的接口:
package com.h3c.dao;
import com.h3c.bean.Student;
import java.util.List;
public interface StudentDao {
void insertStudent(Student student);
void deleteStudentById(int id);
void updateStudent(Student student);
Student selectStudentById(int id);
List<Student> selectAllStudents();
List<Student> selectStudentByName(String name);
}
4.添加mapper映射文件
映射文件,一般称之为mapper,主要是在里面编写SQL语句。dtd约束可以直接拷贝下面的内容。mapper的文件名随意,一般会跟dao接口放在同一个包下,这里映射文件名称定为StudentMapper.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="mybatis">
<!--parameterType可省略,MyBatis会自动识别-->
<insert id="insertStudent" parameterType="com.h3c.bean.Student">
INSERT INTO t_student(name,age,score) VALUES (#{name},#{age},#{score});
<!--获取主键(主键使用自增的策略,当数据插入数据库后才会生成主键)-->
<selectKey resultType="int" keyProperty="id" order="AFTER">
SELECT @@identity
</selectKey>
</insert>
<delete id="deleteStudentById">
DELETE from t_student where id=#{id}
</delete>
<update id="updateStudent">
UPDATE t_student set name=#{name},age=#{age},score=#{score} where id=#{id}
</update>
<!--resultType表示该SQL语句返回的单条数据对应的类型,这里写mybatis.xml文件中配置的别名,首字母大小写均可(若配置文件中未配置,需要写实体类的全限定名)-->
<select id="selectAllStudents" resultType="student">
select id,name,age,score from t_student
</select>
<select id="selectStudentById" resultType="Student">
select id,name,age,score from t_student where id=#{id}
</select>
<!--模糊查询 注意:’%’ #{name} ‘%’之间是没有+号的,即这里的字符串拼接不需要+号,而是需要空格。-->
<select id="selectStudentByName" resultType="student">
SELECT id,name,age,score FROM t_student where name like '%' #{name} '%'
</select>
</mapper>
模糊查询的另一种写法
<select id="selectStudentByName" resultType="student">
SELECT id,name,age,score FROM t_student where name like '%${value}%'
</select>
因为我们传入的name是String字符串类型,所以这里需要注意的是${}中的内容只能写value,可以通过控制台打印的sql语句看出来,这种方式其实就是是字符串拼接,该方式可能会有SQL注入的问题。
#和$区别
- #其实是占位符,通过控制台打印的sql语句可以看出,他是以?进行占位的,类似JDBC的PreparedStatement,可以防止SQL注入的问题,在上面的sql语句中我们写的是#{id},实际上#{}里面的内容可以写成其他字符串#{xxx},这里只是起着占位符的作用,mybatis会将sqlSession.selectList或sqlSession.selectOne等方法中的第二个参数赋值进去。因此如果sql语句需要获取用户的输入从而进行动态拼接的话,就需要使用#{}。
- $是字符串拼接,参数会被直接拼接到SQL语句中,该方式会有SQL注入问题,如果SQL语句由我们程序员直接写好,不需要用户输入的话,可以使用${},不过一般还是建议使用#{}。
5.添加数据库相关配置文件(指定数据源)
在resources目录下创建db.properties配置文件,文件名随意:
#mysql数据库驱动8版本以上添加如下驱动类,5版本是com.mysql.jdbc.Driver
jdbc.driver=com.mysql.cj.jdbc.Driver
#注意:#mysql数据库驱动8版本以上需要指定时区
jdbc.url=jdbc:mysql://127.0.0.1:3306/learnmybatis?useSSL=false&serverTimezone=Asia/Shanghai
jdbc.user=root
jdbc.password=12345
6.添加mybatis的主配置文件,引入上面配置的数据源
主配置文件的名称可以随意命名,这里将其命名为mybatis.xml,将其放到maven项目中的resources目录下。
<?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>
<!--1、注册数据库配置文件-->
<properties resource="db.properties"/>
<!--注意:MyBatis配置文件中的标签是有顺序的-->
<!--2、注册实体类的全限定名的别名-->
<!--我们还可以在实体类上使用@Alias注解来手动给他取一个别名-->
<!--另外mybatis已经为一些常见的类内建了别名-->
<typeAliases>
<!--方式一:不方便-->
<!--<typeAlias type="com.h3c.bean.Student" alias="student"/>-->
<!--方式二:MyBatis会在这个包下搜索需要的bean,生成全限定名的别名(默认是小写字母开头的实体类名)-->
<package name="com.h3c.bean"/>
</typeAliases>
<!--配置MyBatis运行环境,即数据源与事务管理器-->
<!--environments中可进行多环境配置,实际使用的环境由default属性决定-->
<environments default="dev">
<environment id="dev">
<!--指定MyBatis所使用的事务管理器-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--注册映射文件-->
<mapper resource="com/h3c/dao/StudentMapper.xml"/>
</mappers>
</configuration>
- transactionManager标签
该标签用于指定MyBatis所使用的事务管理器。 MyBatis 支持两种事务管理器类型:
- JDBC
该事务器就是我们之前通过Connection的commit()方法提交,通过rollback()方法回滚,默认是需要手动提交的。 - MANAGED
由容器来管理事务的整个事务的生命周期,默认情况下会关闭连接,将来学习spring框架之后,就不用在配置事务管理器了,spring会使用自带的管理器。
- dataSource标签
该项主要用于配置数据源和数据库连接基本属性,有以下三种内建的数据源类型:
- UNPOOLED
该配置表示不使用连接池,每次请求都会创建一个数据库连接,使用完毕后再关闭。当项目对数据库性能要求不大的时候,可以使用该配置。 - POOLED
改配置表示使用mybatis自带的数据库连接池,可以在dataSource下的property属性中设置数据库连接池的基本信息,该部分可以在mybatis的官网中看到。 - JNDI
配置外部数据源
- mapper映射器
MyBatis 是基于 sql 映射配置的框架,sql 语句都写在 Mapper 配置文件中,当构建 SqlSession 类之后,就需要去读取 Mapper 配置文件中的 sql 配置。
而映射器的作用就是在告诉mybatis.xml配置文件我们编写的mapper所在的路径,一共有四种写法:
第一种:相对路径配置
<mappers>
<!--使用相对于类路径的资源引用-->
<mapper resource="com/h3c/dao/StudentMapper.xml"/>
</mappers>
第二种:使用URL绝对路径方式引入(不用)
<mappers>
<!--通过url引入网络资源或者本地磁盘资源-->
<mapper url="F:///h3c/StudentMapper.xml"/>
</mappers>
第三种:类注册引入
该方式的使用,需要满足以下几个要求:
(1)映射文件名要与 Dao 接口名称相同
(2)映射文件要与接口在同一包中
(3)映射文件中的 namespace 属性值为 Dao 接口的全类名
<mappers>
<!--使用mapper标签的class属性指定mapper接口名称-->
<mapper class="com.monkey1024.dao.StudentDao"/>
</mappers>
第四种:接口所在包
这种方式的使用需要满足以下几个条件:
(1)映射文件名要与 Dao 接口名称相同
(2)映射文件要与接口在同一包中
(3)映射文件中的 namespace 属性值为 Dao 接口的全类名
<mappers>
<!--通过package标签的name属性指定mapper接口所在的包名-->
<package name="com.monkey1024.dao"/>
</mappers>
7.查看mybatis输出日志
mybatis支持下面的日志处理:
- slf4j
- Apache Commons Logging
- Log4j
- Log4j 2
- JDK logging
在resources目录下创建log4j.properties文件,里面加上下面内容:
#log4j.rootLogger=debug,console
#只查看某个namespace(mybatis是在StudentMapper.xml中指定的)下的日志
log4j.logger.mybatis=debug,console
#控制台附加器
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern= [%-5p][%d{yyyy-MM-dd HH:mm:ss}]%m%n
8.创建获取SqlSession对象的工具类
其中用到的一些类:
- SqlSessionFactoryBuilder
作用就是创建SqlSessionFactory,在创建SqlSessionFactory之后就不再需要SqlSessionFactoryBuilder了。 - SqlSessionFactory
SqlSessionFactory是线程安全的,在应用中只创建一个SqlSessionFactory即可,即最好保证其是单例的,通过SqlSessionFactory我们可以创建出SqlSession。 - SqlSession
SqlSession是线程不安全的,在后面跟spring进行集成之后,可以创建出线程安全的SqlSession对象。如果单独使用的话,别忘了关闭SqlSession对象。
package com.h3c.util;
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;
/*
* DCL单例模式,这里只是将单例模式应用在创建SqlSessionFactory对象上
*/
public class MyBatisUtil {
private static volatile SqlSessionFactory sqlSessionFactory;
public static SqlSession getSqlSession() {
try {
if(null == sqlSessionFactory) {
//读取主配置文件
InputStream input = Resources.getResourceAsStream("mybatis.xml");
synchronized (MyBatisUtil.class) {
if(null == sqlSessionFactory) {
//创建SqlSessionFactory对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
//创建SqlSession对象并返回
return sqlSessionFactory.openSession();
}
}
9.创建DAO接口的实现类
创建StudentDao接口的实现类StudentDaoImpl:
package com.h3c.dao.impl;
import com.h3c.bean.Student;
import com.h3c.dao.StudentDao;
import com.h3c.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class StudentDaoImpl implements StudentDao {
@Override
public void insertStudent(Student student) {
try (SqlSession sqlSession = MyBatisUtil.getSqlSession()){
//新增数据操作
sqlSession.insert("insertStudent", student);
/*
*当执行插入操作的时候,可以看到在提交事务之前,其实就已经获取到这个主键id了,
* 就是说不管是提交还是回滚,这个主键都是先获取到的。当执行sql语句之后,数据
* 库就会给该条数据分配一个主键,倘若回滚的话,这个主键就不能用了,下次再执行
* 插入操作时,会在该id之后再分配一个id。
* 由此可以得出一个结论:
* 主键的生成跟事务没有关系,只要执行了sql语句,mysql就会为其分配一个主键。
*/
System.out.println("提交数据库之前:" + student);
//提交SqlSession
sqlSession.commit();
}
}
@Override
public void deleteStudentById(int id) {
try(SqlSession sqlSession = MyBatisUtil.getSqlSession()) {
sqlSession.delete("deleteStudentById", id);
sqlSession.commit();
}
}
@Override
public void updateStudent(Student student) {
try(SqlSession sqlSession = MyBatisUtil.getSqlSession()) {
sqlSession.update("updateStudent",student);
sqlSession.commit();
}
}
@Override
public Student selectStudentById(int id) {
Student student = null;
try(SqlSession sqlSession = MyBatisUtil.getSqlSession()) {
//根据id查询数据操作
student = sqlSession.selectOne("selectStudentById", id);
}
return student;
}
@Override
public List<Student> selectAllStudents() {
List<Student> result = null;
try(SqlSession sqlSession = MyBatisUtil.getSqlSession()) {
//查询数据操作
result = sqlSession.selectList("selectAllStudents");
}
return result;
}
@Override
public List<Student> selectStudentByName(String name) {
List<Student> result = null;
try(SqlSession sqlSession = MyBatisUtil.getSqlSession()) {
//查询数据操作
result = sqlSession.selectList("selectStudentByName",name);
}
return result;
}
}
10.使用了junit测试:
在src/test/java目录下新建测试类:
import com.h3c.bean.Student;
import com.h3c.dao.StudentDao;
import com.h3c.dao.impl.StudentDaoImpl;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
public class StudentTest {
private StudentDao studentDao;
@Before
public void initStudentDao(){
studentDao = new StudentDaoImpl();
}
/**
* 插入
*/
@Test
public void insertStudent(){
Student student = new Student("Lucy", 18, 95.50);
//此时主键未生成,id是默认值0
System.out.println("before:" + student);
studentDao.insertStudent(student);
/*
数据已写入数据库,mybatis会主动执行SELECT @@identity 语句帮我们将该数据的主键查询出来,
此时可以获取到id的值,不用自己单独再去数据库查
*/
System.out.println("after:" + student);
}
/**
* 删除
*/
@Test
public void deleteStudent(){
//删除id是7的数据
studentDao.deleteStudentById(7);
}
/**
* 更新
*/
@Test
public void updateStudent(){
Student student = new Student("Lily", 16, 96.50);
student.setId(7);
studentDao.updateStudent(student);
}
/**
* 查询所有Student
*/
@Test
public void selectAllStudents(){
List<Student> students = studentDao.selectAllStudents();
students.forEach((student)->{
System.out.println(student);
});
}
/**
* 查询单个Student
*/
@Test
public void selectStudentById(){
Student student = studentDao.selectStudentById(7);
System.out.println(student);
}
/**
* 根据name模糊查询
*/
@Test
public void selectStudentByName(){
List<Student> students = studentDao.selectStudentByName("L");
students.forEach((student)->{
System.out.println(student);
});
}
}