本文是动力节点MyBatis教程的学习笔记。
第一章 1. 三层架构 (1) 三层的功能-
表示层(User Interface Layer):接受用户数据,显示请求的处理结果,包括jsp、html、servlet等。对应controller包;
-
业务逻辑层(Business Logic Layer):接受表示层传递过来的数据,检查数据,计算业务逻辑,调用数据访问层获取数据。对应service包;
-
数据访问层(Data Access Layer,DAL):也称持久层,与数据库打交道。对应DAO包。
用户使用表示层 --> 业务逻辑层 --> 数据访问层 --> 数据库
(3) 三层对应的处理框架表示层 ---- servlet ---- SpringMVC框架
业务逻辑层 ---- service类 ---- Spring框架
数据访问层 ---- DAO类 ---- MyBatis框架
2. JDBC编程 (1) JDBC实例public void findStudent() {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try { //注册mysql驱动
Class.forName("com.mysql.jdbc.Driver");
// 连接数据的基本信息 url ,username,password
String url = "jdbc:mysql://localhost:3306/springdb";
String username = "root";
String password = "123456";
// 创建连接对象
conn = DriverManager.getConnection(url, username, password);
// 保存查询结果
List stuList = new ArrayList<>();
// 创建Statement, 用来执行sql语句
stmt = conn.createStatement();
// 执行查询,创建记录集,
rs = stmt.executeQuery("select * from student");
while (rs.next()) {
Student stu = new Student();
stu.setId(rs.getInt("id"));
stu.setName(rs.getString("name"));
stu.setAge(rs.getInt("age"));
// 从数据库取出数据转为Student对象,封装到List集合
stuList.add(stu);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
//关闭资源
if (rs != null) ;
{
rs.close();
}
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
(2) JDBC缺点
- 代码较多,开发效率低
- 需要关注Connection、Statement、ResultSet对象的创建和销毁
- 对ResultSet查询的结果,需要自己封装为List
- 重复的代码多
- 业务代码和数据库操作混在一起
SQL映射:可以把数据库表中的一行数据,映射为一个java对象,操作这个对象,就相当于操作表中的数据。
数据访问:对数据库执行增删改查
- 注册数据库的驱动
- 创建 JDBC中必须使用的Connection、Statement、ResultSet对象
- 从 xml中获取中获取sql,执行sql语句,把ResultSet结果转换为java对象
- 关闭资源
项目名为01-helloMyBatis,目录的结构如下:
1) 创建student表并插入数据在MySQL的mybatis库中创建student表:
CREATE TABLE `student` (
`id` int NOT NULL,
`name` varchar(80) NOT NULL,
`email` varchar(100) NOT NULL,
`age` int NOT NULL,
PRIMARY KEY (`id`,`age`)
) ENGINE=InnoDB;
INSERT INTO `student` VALUES ('1001', '李四', 'lisi@qq.com', '11');
INSERT INTO `student` VALUES ('1002', '张三', 'zhangsan@126.com', '12');
2) 加入maven的MyBatis坐标,MySQL驱动的坐标
创建maven工程,在pom.xml文件中添加以下依赖
org.mybatis mybatis 3.4.5 mysql mysql-connector-java 8.0.21 org.projectlombok lombok 1.18.12
其中版本号可以不一样,lombok提供的@Data注解可以为实体类添加get、set、toString方法等,以简化代码。
3) 创建实体类Studentpackage cn.ecnu.domain;
import lombok.Data;
// 推荐和表名一样,方便记忆
@Data
public class Student {
// 定义属性,目前要求属性名和列名一致
private Integer id;
private String name;
private String email;
private Integer age;
}
4) 创建持久层的DAO接口,定义操作数据库的方法
package cn.ecnu.dao;
import cn.ecnu.domain.Student;
import java.util.List;
// 接口操作student表
public interface StudentDAO {
// 查询student表的所有的数据
public List selectStudents();
}
5) 创建一个MyBatis使用的配置文件
也叫做SQL映射文件:写sql语句,一般一个表一个sql映射文件,为xml文件
6) 创建MyBatis的主配置文件
一个项目一个主配置文件,主配置文件提供了数据库的链接信息和sql映射文件的位置信息
写好mybatis的配置文件后,resources下面的资源会在maven的compile的时候自动打包到target目录下,但StudentDAO.xml不会自动打包,需要在pom.xml中设置要扫描的配置文件的位置
7) 创建使用MyBatis类src/main/java ***.xml false
通过MyBatis访问数据库
package cn.ecnu;
import cn.ecnu.domain.Student;
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 MyApp {
public static void main(String[] args) throws IOException {
// 访问mybatis读取student数据
// 1. 定义mybatis主配置文件的名称,从类路径的根开始(target/classes)
String config="mybatis.xml";
// 2. 读取config表示的文件
InputStream in = Resources.getResourceAsStream(config);
// 3. 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
// 4. 创建SqlSessionFactory对象
SqlSessionFactory factory=builder.build(in);
// 5. 【重要】获取SqlSession对象,从SqlSessionFactory中获取SqlSession
SqlSession sqlSession = factory.openSession();
// 6. 【重要】指定要执行的sql语句的标识,sql映射文件中的namespace + "." + 标签的id值
String sqlId="cn.ecnu.dao.StudentDAO"+"."+"selectStudents";
// 7. 执行sql语句,通过sqlId找到语句
List studentList=sqlSession.selectList(sqlId);
// 8. 输出结果
studentList.forEach(System.out::println);
// 9. 关闭SqlSession对象
sqlSession.close();
}
}
运行得到的输出结果就是数据库中插入的两条数据。
(3) 插入数据在StudentDAO.java接口中添加插入对应的方法:
// 插入方法 public int insertStudent(Student student);
在StudentDAO.xml中添加插入方法对应的sql语句:
insert into student values(#{id},#{name},#{email},#{age});
其中#{属性名}是从insertStudent方法的入参student类中取得的属性值。
MyApp.java的6、7、8步需要修改,其他不变:
// 6. 【重要】指定要执行的sql语句的标识,sql映射文件中的namespace + "." + 标签的id值
String sqlId = "cn.ecnu.dao.StudentDAO" + "." + "insertStudent";
// 7. 执行sql语句,通过sqlId找到语句
Student student = new Student();
student.setId(1003);
student.setName("张飞");
student.setEmail("zhangfei@sina.com");
student.setAge(21);
int num = sqlSession.insert(sqlId, student);
// mybatis默认是不自动提交事务的,所以insert、update、delete后要手动提交事务
sqlSession.commit();
// 8. 输出结果
System.out.println("执行insert的结果: " + num);
第二章
1. 主要类的介绍
(1) Resources
mybatis中的一个类,负责读取主配置文件
InputStream in = Resources.getResourceAsStream(config);(2) SqlSessionFactoryBuilder
创建SqlSessionFactory对象
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in);(3) SqlSessionFactory
重量级对象,程序创建一个对象耗时较长,使用资源比较多。在整个项目中,有一个就够用了
SqlSessionFactory接口的实现类为DefaultSqlSessionFactory
SqlSessionFactory作用:获取SqlSession对象
SqlSession sqlSession = factory.openSession();
openSession()方法说明:
- openSession():无参数的,获取非自动提交事务的SqlSession对象
- openSession(boolean):当入参为true时,获取自动提交事务的SqlSession,反之为非自动提交事务的
SqlSession接口:定义了操作数据的方法,例如selectOne()、selectList()、insert()、update()、delete()、commit()、rollback()等
SqlSession接口的实现类为DefaultSqlSession
使用需求:SqlSession对象不是线程安全的,需要在方法内部使用,在执行sql语句之前,使用openSession()获取SqlSession对象,在执行完sql语句之后,需要执行SqlSession.close()来关闭它,这样能保证它的使用是线程安全的
2. 传统DAO使用方式为StudentDAO接口增加对应的实现类,把业务逻辑代码放到实现类里,而不是直接放到main方法或者单元测试方法中。项目名为02-MyBatisUtils,代码组织结构如下:
实现类文件StudentDAOImpl.java中的内容为:
package cn.ecnu.dao.impl;
import cn.ecnu.dao.StudentDAO;
import cn.ecnu.domain.Student;
import cn.ecnu.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class StudentDAOImpl implements StudentDAO {
@Override
public List selectStudents() {
// 获取SqlSession
SqlSession sqlSession = MyBatisUtils.getSqlSession();
//【重要】指定要执行的sql语句的标识,sql映射文件中的namespace + "." + 标签的id值
String sqlId = "cn.ecnu.dao.StudentDAO" + "." + "selectStudents";
// 执行sql语句,通过sqlId找到语句
List studentList = sqlSession.selectList(sqlId);
// 关闭SqlSession对象
sqlSession.close();
return studentList;
}
@Override
public int insertStudent(Student student) {
// 获取SqlSession
SqlSession sqlSession = MyBatisUtils.getSqlSession();
//【重要】指定要执行的sql语句的标识,sql映射文件中的namespace + "." + 标签的id值
String sqlId = "cn.ecnu.dao.StudentDAO" + "." + "insertStudent";
// 执行sql语句,通过sqlId找到语句
int num = sqlSession.insert(sqlId, student);
// mybatis默认是不自动提交事务的,所以insert、update、delete后要手动提交事务
sqlSession.commit();
return num;
}
}
单元测试文件StudentTest.java中的内容为:
package cn.ecnu;
import cn.ecnu.dao.StudentDAO;
import cn.ecnu.dao.impl.StudentDAOImpl;
import cn.ecnu.domain.Student;
import org.junit.Test;
import java.util.List;
public class StudentTest {
@Test
public void selectStudentsTest() {
StudentDAO dao = new StudentDAOImpl();
List studentList = dao.selectStudents();
studentList.forEach(System.out::println);
}
@Test
public void insertStudentTest() {
Student student = new Student();
student.setId(1004);
student.setName("刘备");
student.setEmail("liubei@sina.com");
student.setAge(22);
StudentDAO dao = new StudentDAOImpl();
int num = dao.insertStudent(student);
System.out.println("insert操作的执行结果:" + num);
}
}
其他文件和之前的保持一致。
第三章 1. 动态代理使用SqlSession.getMapper(dao接口.class)获取这个dao接口的对象。
项目名为03-proxy-dao,目录结构如下:
相比于02-MyBatisUtils,删除了实现类文件StudentDAOImpl.java,将StudentTest.java修改为:
import cn.ecnu.dao.StudentDAO;
import cn.ecnu.domain.Student;
import cn.ecnu.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class StudentTest {
@Test
public void selectStudentsTest() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
// 调用dao的方法,执行数据库的操作
List students = dao.selectStudents();
for (Student stu : students) {
System.out.println("学生: " + stu);
}
}
@Test
public void insertStudentTest() {
Student student = new Student();
student.setId(1005);
student.setName("关羽");
student.setEmail("guanyu@sina.com");
student.setAge(22);
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
int num = dao.insertStudent(student);
System.out.println("insert操作的执行结果:" + num);
}
}
2. 传入参数
从java代码中把数据传入到mapper文件的sql语句中。parameterType:写在mapper文件中的一个属性,表示dao接口中的
(1) 一个简单类型的参数mybatis把java的基本数据类型和String类型都叫做简单类型。在mapper文件获取简单类型的一个参数的值,使用 #{任意字符}
项目名为04-param,目录结构为:
StudentDAO.java的内容修改为:
package cn.ecnu.dao;
import cn.ecnu.domain.Student;
import java.util.List;
// 接口操作student表
public interface StudentDAO {
// 根据id查询
public Student selectStudentById(Integer id);
}
StudentDAO.xml中的内容修改为:
select id,name,email,age from student where id=#{id};
StudentTest.java中的内容修改为:
package cn.ecnu;
import cn.ecnu.dao.StudentDAO;
import cn.ecnu.domain.Student;
import cn.ecnu.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class StudentTest {
@Test
public void selectStudentByIdTest() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
// 调用dao的方法,执行数据库的操作
Student student = dao.selectStudentById(1002);
System.out.println(student);
}
}
(2) 多个参数 - 使用@Param
使用@Param命名参数。在StudentDAO.java中添加:
ListselectMultiPram(@Param("myname") String name, @Param("myage") Integer age);
在StudentDAO.xml中添加:
在StudentTest.java中添加:
@Test
public void selectMultiParmTest() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
// 调用dao的方法,执行数据库的操作
List studentList = dao.selectMultiPram("张飞", 22);
for (Student student : studentList) {
System.out.println(student);
}
}
(3) 多个参数 - 使用对象
添加查询参数的实体类cn.ecnu.vo.QueryParam.java,其内容为:
package cn.ecnu.vo;
import lombok.Data;
@Data
public class QueryParam {
private String paramName;
private Integer paramAge;
}
在StudentDAO.java中添加:
ListselectMultiObject(QueryParam param);
在StudentDAO.xml中添加:
在StudentTest.java中添加:
@Test
public void selectMultiObjectTest() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
QueryParam param = new QueryParam();
param.setParamName("张三");
param.setParamAge(21);
// 调用dao的方法,执行数据库的操作
List studentList = dao.selectMultiObject(param);
for (Student student : studentList) {
System.out.println(student);
}
}
(4) 多个参数 - 按位置
在StudentDAO.xml中使用:
- mybatis3.4之前:#{0}、#{1}等
- mybatis3.4之后:#{arg0}、#{arg1}等
了解即可,容易出错,不建议用。
(5) 多个参数 - 使用MapStudentDAO.java中的形参为一个Map
StudentDAO.xml中使用#{map的key}
了解即可。
(6) #{}和${}的区别- #使用?占位符在sql语句中做占位,使用PreparedStatement执行sql,效率高
- #能够避免sql注入,更安全
- $不使用占位符,是字符串连接方式,使用Statement对象执行sql,效率低
- $有sql注入的风险,缺乏安全性
- $可以替换表名或列名
mybatis执行了sql 语句,得到java对象
resultType结果类型:执行了sql语句之后,数据转为的java对象,该对象的类型是任意的,可以是实体类,也可以是int类型等。
处理方式:
- mybatis执行sql语句,然后调用类的无参构造方法,创建对象
- mybatis把ResultSet指定列值赋给同名的属性
resultType的值:
- 全限定类名
- 类型的别名,如java.lang.Integer别名是int
创建05-returnResult项目,在mybatis主配置文件中定义,使用
在mybatis.xml中的
然后在StudentDAO.xml中将resultType中的内容改为student,运行单元测试和之前结果一样。
返回类型还可以设为Map
(3) resultMapresultMap和resultType不能同时使用
结果映射, 指定列名和java对象属性的对应关系,使用场景:
- 自定义列赋值给哪个属性
- 当列名和属性名不一致时,必须使用resultMap
在StudentDAO.java中添加:
ListselectAllStudents();
在StudentDAO.xml中的mapper标签下添加:
在StudentTest.java中添加:
@Test
public void selectAllStudentsTest() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
// 调用dao的方法,执行数据库的操作
List studentList = dao.selectAllStudents();
for (Student student : studentList) {
System.out.println(student);
}
}
(4) 处理字段名和属性名不同的第二种方法
创建MyStudent.java实体类:
package cn.ecnu.domain;
import lombok.Data;
// 推荐和表名一样,方便记忆
@Data
public class MyStudent {
// 定义属性,目前要求属性名和列名一致
private Integer stuId;
private String stuName;
private String stuEmail;
private Integer stuAge;
}
在StudentDAO.java中添加:
ListselectMyStudent();
在StudentDAO.xml中添加:
在StudentTest.java中添加:
@Test
public void selectMyStudentTest() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
// 调用dao的方法,执行数据库的操作
List studentList = dao.selectMyStudent();
for (MyStudent student : studentList) {
System.out.println(student);
}
}
(5) 模糊查询的两种方式
在StudentDAO.java中添加:
ListselectLikeOne(String name); List selectLikeTwo(String name);
在StudentDAO.xml中添加:
在StudentTest.java中添加:
@Test
public void selectLikeOneTest() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
// 准备好like的内容
String name = "%李%";
List studentList = dao.selectLikeOne(name);
for (Student student : studentList) {
System.out.println(student);
}
}
@Test
public void selectLikeTwoTest() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
// 准备好like的内容
String name = "张";
List studentList = dao.selectLikeTwo(name);
for (Student student : studentList) {
System.out.println(student);
}
}
第四章 动态SQL
动态sql指的是sql的内容是变化的,可以根据条件获取到不同的sql语句,主要是where部分发生变化。
动态sql的实现,使用的是mybatis提供的标签:
语法:
部分sql语句
在StudentDAO.java中添加:
// 动态sql,使用java对象作为参数 ListselectStudentIf(Student student);
在StudentDAO.xml中添加:
select id,name,email,age from student where 1 = 1 and name = #{name} or age > #{age}
在StudentTest.java中添加:
@Test
public void selectStudentIfTest() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
Student student = new Student();
student.setName("李四");
student.setAge(20);
// 调用dao的方法,执行数据库的操作
List studentList = dao.selectStudentIf(student);
for (Student stu : studentList) {
System.out.println(stu);
}
}
2. 用来包含多个
在StudentDAO.java中添加:
// where的使用 ListselectStudentWhere(Student student);
在StudentDAO.xml中添加:
select id,name,email,age from student name = #{name} or age > #{age}
在StudentTest.java中添加:
@Test
public void selectStudentWhereTest() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
Student student = new Student();
student.setAge(20);
// 调用dao的方法,执行数据库的操作
List studentList = dao.selectStudentWhere(student);
for (Student stu : studentList) {
System.out.println(stu);
}
}
3. 循环java中的数组,list集合的。主要用在sql的in语句中。比如查询学生id为1001、1002、1003的三个学生。
-
collection:接口中方法参数的类型,如果是数组则使用array,如果是List则使用list
-
item:自定义的,表示数组和集合成员的变量
-
open:循环开始时的字符
-
close:循环结束时的字符
-
separator:集合成员之间的分隔符
在StudentDAO.java中添加:
ListselectStudentForeachOne(List idList);
在StudentDAO.xml中添加:
select id,name,email,age from student where id in #{myid}
在StudentTest.java中添加:
@Test
public void selectStudentForeachOneTest() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
List idList = new ArrayList<>();
idList.add(1001);
idList.add(1002);
idList.add(1003);
// 调用dao的方法,执行数据库的操作
List studentList = dao.selectStudentForeachOne(idList);
for (Student stu : studentList) {
System.out.println(stu);
}
}
在StudentDAO.java中添加:
ListselectStudentForeachTwo(List Students);
在StudentDAO.xml中添加:
select id,name,email,age from student where id in #{student.id}
在StudentTest.java中添加:
@Test
public void selectStudentForeachTwoTest() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
List students = new ArrayList<>();
Student s1 = new Student();
s1.setId(1001);
students.add(s1);
Student s2 = new Student();
s2.setId(1002);
students.add(s2);
Student s3= new Student();
s3.setId(1003);
students.add(s3);
// 调用dao的方法,执行数据库的操作
List studentList = dao.selectStudentForeachTwo(students);
for (Student stu : studentList) {
System.out.println(stu);
}
}
4. 代码片段
复用一些sql语句
先定义
sql语句、表名、字段名等
再使用
在StudentDAO.xml中添加:
select id,name,email,age from student
第五章 1. 数据库的属性配置文件where id in #{student.id}
把数据库连接信息放到一个单独的文件中,和mybatis主配置文件分开,目的是便于修改、保存、处理多个数据库的信息。
-
在resources目录定义一个属性配置文件,xxx.properties,例如jdbc.properties。在属性配置文件中,定义数据,格式是 key = value。key一般使用.做多级目录,例如 jdbc.mysql.driver
# jdbc.properties文件中的内容 jdbc.driver = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://localhost:3306/mybatis jdbc.username = root jdbc.password = root
-
在mybatis的主配置文件中,使用
标签指定文件的位置,在需要使用值的地方,使用${}
当有多个mapper文件时,在mybatis的主配置文件中有两种导入方式
方式一:
方式二:
注意这两种方式不能并存,否则会报错。
第六章PageHelper:做数据分页的
在pom.xml文件中添加依赖:
com.github.pagehelper pagehelper 5.1.10
在mybatis主配置文件mybatis.xml中添加:
在StudentDAO.java中添加接口方法:
// 使用PageHelper分页插件 ListselectAll();
在StudentDAO.xml中添加对应的SQL,注意此处不能以分号结尾,否则字符串拼接时会出错:
select * from student order by id
在StudentTest.java中添加单元测试文件:
@Test
public void selectAllTest() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
PageHelper.startPage(1, 2);
List studentList = dao.selectAll();
for (Student stu : studentList) {
System.out.println(stu);
}
}



