来源:动力节点MyBatis教程实战精讲视频及文档,再次表示感谢!!
https://www.bilibili.com/video/BV185411s7Ry?spm_id_from=333.999.0.0&vd_source=75a98c11a89e81de728ed7f28e735d32
本文PDF版下载链接,以及相关资料链接: 链接: https://pan.baidu.com/s/1e4SiPgKqrrv7ekOiX727pA 提取码: wl55
第一章 框架概述
1.1 软件开发常用的结构
1.1.1 三层架构
三层架构包含的三层: 界面层(User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer)
-
界面层(表示层,视图层):主要功能是接受用户的数据,显示请求的处理结果。使用 web 页面和 用户交互,手机 app 也就是表示层的,用户在 app 中操作,业务逻辑在服务器端处理。-----> jsp html -
业务逻辑层:接收表示传递过来的数据,检查数据,计算业务逻辑,调用数据访问层获取数据。 -----> service -
数据访问层:与数据库打交道。主要实现对数据的增、删、改、查。将存储在数据库中的数据提交 给业务层,同时将业务层处理的数据保存到数据库. -----> dao
三层对应的包:
-
controller包 (servlet) -
service包 (xxxService) -
dao包(xxxDao)
三层类的交互关系:
? 用户—> 界面层—>业务逻辑层—>数据访问层—>DB 数据库
为什么要使用三层架构:
-
结构清晰、耦合度低, 各层分工明确 -
可维护性高,可扩展性高 -
有利于标准化 -
开发人员可以只关注整个结构中的其中某一层的功能实现 -
有利于各层逻辑的复用
三层对应的处理框架
界面层 — servlet — springmvc
业务逻辑层----service—spring
数据访问层— dao ---- mybatis
1.2 框架是什么
1.2.1 框架定义
? 框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法 另一种认为,框架是可被应用开发者定制的应用骨架、模板。
? 简单的说,框架其实是半成品软件,就是一组组件,供你使用完成你自己的系统。从另一个角度来 说框架一个舞台,你在舞台上做表演。在框架基础上加入你要完成的功能。
? 框架是安全的,可复用的,不断升级的软件。
1.2.2 框架特点
- 不是全能的
- 框架是针对某一个邻域有效,特长在某一个方面
- 框架是别人写好的程序
1.2.3 框架解决要解决的问题
? 框架要解决的最重要的一个问题是技术整合,在 J2EE 的 框架中,有着各种各样的技术,不同的应 用,系统使用不同的技术解决问题。需要从 J2EE 中选择不同的技术,而技术自身的复杂性,有导致更大的风险。企业在开发软件项目时,主要目的是解决业务问题。 即要求企业负责技术本身,又要求解决业务问题。这是大多数企业不能完成的。框架把相关的技术融合在一起,企业开发可以集中在业务领 域方面。
? 另一个方面可以提高开发的效率。
1.3 为什么要用MyBatis
? 首先先来回顾一下 JDBC 以及 JDBC 存在的问题
1.3.1 JDBC编程方式
? 使用JDBC做一个查询
import java.sql.*;
public class SelectRecords {
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/jdbc_db";
static final String USER = "root";
static final String PASS = "123456";
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try{
Class.forName("com.mysql.jdbc.Driver");
System.out.println("Connecting to a selected database...");
conn = DriverManager.getConnection(DB_URL, USER, PASS);
System.out.println("Connected database successfully...");
System.out.println("Creating statement...");
stmt = conn.createStatement();
String sql = "SELECT id, name, email, age FROM student";
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()){
int id = rs.getInt("id");
int age = rs.getInt("age");
String first = rs.getString("first");
String last = rs.getString("last");
System.out.print("ID: " + id);
System.out.print(", Age: " + age);
System.out.print(", First: " + first);
System.out.println(", Last: " + last);
}
rs.close();
}catch(SQLException se){
se.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(stmt!=null)
conn.close();
}catch(SQLException se){
}
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}
}
System.out.println("Goodbye!");
}
}
1.3.2 JDBC的缺陷
-
代码冗余,开发效率低 -
需要关注 Connection ,Statement, ResultSet 对象创建和销毁 -
对 ResultSet 查询的结果,需要自己封装为 List -
业务代码和数据库的操作混在一起
1.4 MyBatis框架概述
MyBatis 框架: MyBatis 本是 apache 的一个开源项目 iBatis, 2010 年这个项目由 apache software foundation 迁移到了 google code,并且改名为 MyBatis 。2013 年 11 月迁移到 Github。 iBATIS 一词来源于“internet”和“abatis”的组合,是一个基于 Java 的持久层框架。iBATIS 提供的 持久层框架包括 SQL Maps 和 Data Access Objects(DAOs)
1.4.1 MyBatis框架解决的问题
? 针对JDBC的缺陷,MyBatis减轻使用 JDBC 的复杂性,不用编写重复的创建 Connetion , Statement ; 不用编写关闭资源代码, 直接使用 java 对象;直接表示结果数据,让开发者专注 SQL 的处理。
MyBatis 可以完成:
-
注册数据库的驱动,例如 Class.forName(“com.mysql.jdbc.Driver”)) -
创建 JDBC 中必须使用的 Connection , Statement, ResultSet 对象 -
从 xml 中获取 sql,并执行 sql 语句,把 ResultSet 结果转换 java 对象,省略以下语句
List<Student> list = new ArrayLsit<>();
ResultSet rs = state.executeQuery(“select * from student”);
while(rs.next){
Student student = new Student();
student.setName(rs.getString(“name”));
student.setAge(rs.getInt(“age”));
list.add(student);
}
- 关闭资源
ResultSet.close() , Statement.close() , Conenection.close()
第二章MyBatis框架入门
2.1 入门案例
Ch01-hello-mybatis:第一个入门的mybatis例子
实现步骤:
- 新建一个student表
- 加入Maven的MyBatis坐标 mysql驱动的坐标
- 创建实体类 Student —>对应的表中一行数据
- 创建持久层的dao接口,定义操作数据库的方法
- 创建一个mybatis使用的配置文件
? 叫做sql映射文件:写sql语句的 一般一个表一个sql映射文件 这个文件是xml文件
? 1) 这个xml写在接口所在的目录中
? 2) 文件名称和接口保持一致
- 创建mybatis的主配置文件
? 一个项目就是一个主配置文件
? 主配置文件提供了数据库的连接信息和SQL映射的文件位置信息
- 创建使用mybatis类,
? 通过mybatis访问数据库
接下来进行演示:先看一下项目结构
(1)在数据库中新建一个student表 , springdb库中
CREATE TABLE `student` (
`id` int(11) NOT NULL ,
`name` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
向数据库随便添加两行数据,如图:
(2)加入Maven的MyBatis坐标 mysql驱动的坐标
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
(3)创建实体类 Student —>对应的表中一行数据
创建domain.Student
package com.st.domain;
import jdk.nashorn.internal.objects.annotations.Setter;
@Data
public class Student {
private Integer id;
private String name;
private String email;
private Integer age;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", age=" + age +
'}';
}
}
(4)创建持久层的dao接口,定义操作数据库的方法
创建dao.StudentDao接口,接口中有一个查询方法
package com.st.dao;
import com.st.domain.Student;
import java.util.List;
public interface StudentDao {
public List<Student> selectStudents();
}
(5)创建一个mybatis的配置文件
? 该文件叫做sql映射文件:写sql语句的 ,一般一个表一个sql映射文件,在此处为StudentDao.xml
? 1.这个xml写在接口所在的目录中
? 2.文件名称和接口保持一致
<?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.st.dao.StudentDao">
<select id="selectStudents" resultType=>
select id, name, email, age from student order by id;
</select
</mapper>
mapper: 当前文件的根标签 namespace: 命名空间,唯一值,可以是自定义的字符串 要求你使用dao接口的全限定名称
select: 表示查询语句
- id:要执行的sql语句的唯一标识,mybatis会使用这个id值来找到要执行的sql语句,使用接口中的方法名称
- resultType:表示结果类型的,是sql语句执行后得到Resulset,遍历即可得到java对象的类型
(6)创建mybatis的主配置文件,主配置文件提供了数据库的连接信息和SQL映射的文件位置信息
在resources下创建mybatis.xml文件:
<?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>
<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/springdb"/>
<property name="username" value="root"/>
<property name="password" value="12345678"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="12345678"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/st/dao/StudentDao.xml"/>
</mappers>
</configuration>
在maven中配置扫描规则,pom中加入:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
(7)创建使用mybatis类,通过mybatis访问数据库
在com.st包下创建MyApp类:
package com.st;
import com.st.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 javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyApp {
public static void main(String[] args) throws IOException {
String config = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(config);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
SqlSession sqlSession = factory.openSession();
String sqlId = "com.st.dao.StudentDao" + "." + "selectStudents";
List<Student> studentList = sqlSession.selectList(sqlId);
studentList.forEach(student -> System.out.println(student));
sqlSession.close();
}
}
运行结果: 访问到了数据库,至此完成了MyBatis入门之旅。
2.2 增删改查
? 注意:insert update delete方法需要增加一步提交事务操作!
xml文件中:
1.dao对象,类型是StudentDao,全限定名称:com.st.dao.StudentDao
全限定名称和namespace是一样的。
2.dao调用的方法名称;selectStudents
方法名称和mapper文件中的id值是一样的
3.通过dao中方法的返回值可以确定MyBatis要调用的SqlSession的方法
如果返回值是list, 调用SqlSession.selectList()方法
如果返回值是int, 或是非list,则看mapper文件中的标签是什么
2.2.1 select
在StudentDao中添加方法:
public List<Student> selectStudents();
在StudentDao.xml中添加:
<select id="selectStudents" resultType="com.st.domain.Student">
select id, name, email, age from student order by id;
</select>
测试代码:
@Test
public void selectStudents() throws IOException {
String config = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(config);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
SqlSession sqlSession = factory.openSession();
String sqlId = "com.st.dao.StudentDao" + "." + "selectStudents";
List<Student> studentList = sqlSession.selectList(sqlId);
studentList.forEach(student -> System.out.println(student));
sqlSession.close();
}
结果:
2.2.2 insert
在StudentDao中添加方法:
public int insertStudent(Student student);
在StudentDao.xml中添加:
<insert id="insertStudent">
insert into student values (#{id}, #{name}, #{email}, #{age});
</insert>
测试代码:
@Test
public void insertStudent() throws IOException {
String config = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(config);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
SqlSession sqlSession = factory.openSession();
Student student = new Student();
student.setId(2001);
student.setName("孙悟空");
student.setEmail("swk@xiyou.com");
student.setAge(500);
String sqlId = "com.st.dao.StudentDao" + "." + "insertStudent";
int row = sqlSession.insert(sqlId, student);
System.out.println("影响行数:" + row);
sqlSession.commit();
sqlSession.close();
}
}
结果:
数据库中确实插入成功:
2.2.3 update
在StudentDao中添加方法:
public int updateStudent(Student student);
在StudentDao.xml中添加:
<update id="updateStudent" >
update student set age = #{age} where id = #{id}
</update>
测试代码:
@Test
public void updateStudent() throws IOException {
String config = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(config);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
SqlSession sqlSession = factory.openSession();
Student student = new Student();
student.setId(2001);
student.setAge(1000);
String sqlId = "com.st.dao.StudentDao" + "." + "updateStudent";
int row = sqlSession.update(sqlId, student);
System.out.println("影响行数:" + row);
sqlSession.commit();
sqlSession.close();
}
测试结果:
数据库中确实更新成功:
2.2.4 delete
在StudentDao中添加方法:
public int insertStudent(Student student);
在StudentDao.xml中添加:
<insert id="insertStudent">
insert into student values (#{id}, #{name}, #{email}, #{age});
</insert>
测试代码:
@Test
public void deleteStudent() throws IOException {
String config = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(config);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
SqlSession sqlSession = factory.openSession();
String sqlId = "com.st.dao.StudentDao" + "." + "deleteStudent";
int row = sqlSession.update(sqlId, 2001);
System.out.println("影响行数:" + row);
}
测试结果:
数据库中确实删除成功:
2.3 日志
? 一个语句是否执行成功,在idea中只能获取影响行数来判断,不能详细的看到每个sql语句执行的具体语句是什么,如果发生错误,不容易排查,为此,可使用日志功能来详细的查看每次运行执行的sql语句信息。
在主配置文件中对增加setting标签,位置在标签内部
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
未使用日志:
使用日志:会给出具体的sql语句,方便查找错误
2.4 主要类的介绍
2.4.1 Resources类
? MyBatis中的一个类,主要负责读取主配置文件或资源文件等,其有很多方法通过加载并解析资源文件,可返回不同类型的 IO 流对象。
2.4.2 SqlSessionFactoryBuilder类
? 通过创建SqlSessionFactoryBuilder,使用SqlSessionFactoryBuilder对象的build() 方法,来进一步创建SqlSessionFactory对象。
? 由于SqlSessionFactoryBuilder 对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将 该 SqlSessionFactoryBuilder 对象创建为一个方法内的局部对象,方法结束,对象销毁。
2.4.3 SqlSessionFactory接口
? 重量级对象,重量级对象是创建对象耗时比较长,使用资源比较多,在整个项目中,有一个就够用了。
? SqlSessionFactory:本身是一个接口,接口的实现类 DefaultSqlSessionFactory
? SqlSessionFactory作用:通过openSession方法,获取SqlSession对象
openSession方法说明:
- openSession():无参数的,默认是false,获取的是非自动提交事务的SqlSession对象
- openSession(true):根据参数,获取能否自动提交事务的SqlSession对象
2.4.4 SqlSession接口
? 定义了操作数据的方法,例如selectOne() selectList() insert() update()
SqlSession接口的实现类:DefaultSqlSession
? 使用要求:SqlSession对象不是线程安全的,需要在方法内部使用, 在执行sql语句后需要执行SqlSession.close()来关闭它,避免线程不安全
2.5 创建工具类
? 可以从上文看出,在测试代码中很多代码是重复的,因此可以创建一个工具类,方便代码复用。
2.5.1 创建MyBatisUtil类
? 简而言之,就是将一些公共代码单独做成一个类:
创建一个utils包,在包下创建MyBatisUtils
public class MyBatisUtils {
private static SqlSessionFactory sessionFactory = null;
static {
String config = "mybatis.xml";
try {
InputStream in = Resources.getResourceAsStream(config);
sessionFactory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static SqlSession getSqlSession(){
SqlSession sqlSession = null;
if(sessionFactory != null){
sqlSession = sessionFactory.openSession();
}
return sqlSession;
}
}
2.5.1 使用MyBatisUtils类
? 在测试代码中:
@Test
public void selectStudents() throws IOException {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
String sqlId = "com.st.dao.StudentDao" + "." + "selectStudents";
List<Student> studentList = sqlSession.selectList(sqlId);
studentList.forEach(student -> System.out.println(student));
sqlSession.close();
}
第三章 MyBatis框架Dao代理
? 分析前文的代码执行流程,对于一个StudentDao接口中的方法,在测试类中需要先组装好sqlId(1),再通过sqlSession中对应的方法(2)来调用StudentDao.xml中写好的sql语句。
@Test
public void selectStudents() throws IOException
SqlSession sqlSession = MyBatisUtils.getSqlSession();
String sqlId = "com.st.dao.StudentDao" + "." + "selectStudents";
List<Student> studentList = sqlSession.selectList(sqlId);
studentList.forEach(student -> System.out.println(student));
sqlSession.close();
}
? 这个过程,使用者需要知道:①组装sqlid这个字符串 ②需要知道sqlSession中包含什么方法,将sqlid传入对应的方法中。 而我们期望的是,使用者通过StudentDao定义中的方法,并调用该方法就能实现对应的操作。
? 为了实现这个目的,一般传统的Dao开发方式如下文。
3.1 MyBatis使用传统的Dao开发方式
? 传统的开发方式步骤:
- 创建一个StudentDao接口
- 实现这个SudentDao类 --> StudentDaoImpl,通过SqlSession调用StudentDao.xml
- 编写StudentDao.xml方法
接下来,以selectStudents()方法为主:
(1)在StudentDao接口创建selectStudents()方法
public List<Student> selectStudents();
(2)在Dao中创建impl包,在impl包中实现Dao接口类 StudentDaoImpl
public class StudentDaoImpl implements StudentDao {
@Override
public List<Student> selectStudents() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
String sqlId = "com.st.dao.StudentDao.selectStudents";
List<Student> students = sqlSession.selectList(sqlId);
sqlSession.close();
return students;
}
}
(3)StudentDao.xml是相同的
<mapper namespace="com.st.dao.StudentDao">
<select id="selectStudents" resultType="com.st.domain.Student">
select id, name, email, age from student order by id;
</select>
测试代码:
@Test
public void testSelect(){
StudentDao studentDao = new StudentDaoImpl();
List<Student> students = studentDao.selectStudents();
students.forEach(student -> System.out.println(student));
}
3.2 传统的开发方式分析
? 在前面例子中自定义 Dao 接口实现类时发现一个问题:Dao 的实现类其实并没有干什么实质性的工作,它仅仅就是通过 SqlSession 的相关 API 定位到映射文件 mapper 中相应 id 的 SQL 语句,真正对 DB 进行操作的工作其实是由框架通过 mapper 中的 SQL 完成的。
? 所以,MyBatis 框架就抛开了 Dao 的实现类,直接定位到映射文件 mapper 中的相应 SQL 语句,对 DB 进行操作。这种对 Dao 的实现方式称为 Mapper 的动态代理方式。
MyBatis的动态代理:MyBatis根据dao的方法调用,获取执行sql语句的信息. MyBatis通过dao接口,创建一个dao接口的实现类,并创建这个类的对象,也就是MyBatis可以自动创建一个impl类,完成SqlSession的调用,访问数据库
3.3 MyBatis框架Dao代理方式
3.3.1 步骤
-
去掉Dao接口的实现类 —> StudentDaoImpl -
通过MyBatis框架来获取代理类对象,也就是MyBatis根据接口,自动生成一个Impl实现类。 -
使用获取的Dao实现类来执行sql语句
举例:
(1)去掉Dao接口的实现类 —> StudentDaoImpl
(2)MyBatis框架中getMapper方法来获取代理类对象
(3)使用获取的Dao实现类来执行sql语句
@Test
public void testSelect(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
List<Student> students = studentDao.selectStudents();
students.forEach(student -> System.out.println(student));
sqlSession.close();
}
3.3.2 原理
? 通过Debug方式进行调试,分析原理:
MyBatis通过动态代理的方式试下获取impl类。
ps:由于本人没有找到文档中图片的源码,只在这里贴出文档代码:
MapperProxy 类定义:
invoke方法:
重点方法:
3.3 传入参数
? 从java代码中把数据传入到mapper文件的sql语句中
3.3.1 parameterType
? 写在mapper文件中的一个属性,用来表示dao接口方法中的参数的数据类型。parameterType它的值是java的数据类型全限定名称或者是mybatis定义的别名。 在实际开发或使用中,使用该属性不多,了解即可。
例如StudentDao接口:
public Student selectStudentById(Integer id)
在xml文件中改写:
<mapper namespace="com.st.dao.StudentDao">
<select id="selectStudentById" parameterType="java.lang.Integer" resultType="com.st.domain.Student">
select id, name, email, age from student where id = #{id};
</select>
</mapper>
? parameterType来告诉mybatis传入的 id 是Integer 类型
3.3.2 传入一个简单类型参数
一个简单类型参数:基本数据类型和String叫做简单类型,注意这种方法的限定 ① 一个 ②简单类型参数
接口代码:
public Student selectStudentById(Integer id)
在mapper文件中获取简单类型的一个参数的值,使用#{任意字符},所以在xml文件中id改为其他字符也可以:
select id, name, email, age from student where id = #{studentId};
?
? 使用#{}之后,mybatis执行sql是使用的jdbc中的PreparedStatement对象,由mybatis执行下面的代码:
- mybatis创建Connection,PreparedStatement对象
String sql = "select id,name,email,age from student where id=?"
PreparedStatement pst = conn.preparedStatement(sql);
pst.setInt(1, 1002);
- 执行sql 封装为resultType="com.st.domain.Student"这个对象
ResultSet rs = ps.executeQuery();
Student student = null
while(rs.next()){
Student student = new Student();
student.setId(rs.getInt("id"));
student.setName(rs.getInt("name"));
student.setEmail(rs.getInt("email"));
student.setAge(rs.getInt("age"));
}
return student;
注意事项:
? 使用#号是占位符,能够避免sql注入,在 3.3.7 中有提到
3.3.3 多个参数-使用@Param
? 使用@Param命名参数,给参数起名字。
举例:
Dao接口:
public List<Student> selectMulitParam(String name, Integer age)
使用@Param(“参数名”) String name
public List<Student> selectMulitParam(@Param("myname")String name,@Param("myage")Integer age)
可以在mapper文件中:
select id, name, email, age from student where name = #{myname} or age= #{myage};
3.3.4 多个参数-使用对象
? 使用java对象传递参数,属性值就是sql需要的参数值,一个属性就是一个参数。
使用对象语法:
? #{属性名,javaType=类型名称,jdbcType=数据类型} <!--很少用-->
? - javaType:指java中的属性数据类型
? - jdbcType:在数据库中的数据类型
select id, name, email, age from student where
name = #{paramName, javaType = java.lang.String, jdbcType=VARCHAR} or
age = #{paramAge, javaType = java.lang.Integer, jdbcType=INTEGER}
可以简化为:#{属性名}
select id, name, email, age from student where name=#{paramName} or age=#{paramAge};
使用对象进行参数传递有两种方式:1)新建类方式 2)实体类方式
第一种方法举例:
(1)新建一个QueryParam类,用于传递参数:
package com.st.vo;
@Data
public class QueryParam {
private String paramName;
private Integer paramAge;
}
(2)Dao接口:
public List<Student> selectMultiObject(QueryParam queryParam);
(3)xml文件:
<select id="selectMultiObject" resultType="com.st.domain.Student">
select id, name, email, age from student where name=#{paramName} or age=#{paramAge};
</select>
(4)测试代码:
@Test
public void selectMultiObject() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
QueryParam zs = new QueryParam();
zs.setParamName("张三");
zs.setParamAge(10);
List<Student> studentList = mapper.selectMultiObject(zs);
studentList.forEach(System.out::println);
sqlSession.close();
}
(常用)第二种方法举例:
(1)使用已有的Student实体类,用于传递参数
(2)Dao接口:
public List<Student> selectMultiStudent(Student student);
(3)xml文件:
<select id="selectMultiStudent" resultType="com.st.domain.Student">
select id, name, email, age from student where name=#{name} or age=#{age};
</select>
(4)测试代码:
@Test
public void selectMultiStudent() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
Student zs = new Student();
zs.setName("张三");
zs.setAge(10);
List<Student> studentList = mapper.selectMultiStudent(zs);
studentList.forEach(System.out::println);
sqlSession.close();
}
3.3.5 多个参数-按位置的
? 参数位置从 0 开始, 引用参数语法#{ arg 位置 } ,第一个参数是#{arg0}, 第二个是#{arg1}
注意:mybatis-3.3 版本和之前的版本使用#{0},#{1}方式, 从 mybatis3.4 开始使用#{arg0}方式。
举例:
(1)Dao接口
public List<Student> selectByIndex(String name,Integer age);
(2)xml文件:
<select id="selectByIndex" resultType="com.st.domain.Student">
select id, name, email, age from student where name=#{arg0} or age=#{arg1};
</select>
(3)测试代码:
@Test
public void selectByIndex() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
List<Student> studentList = mapper.selectByIndex("张飞", 15);
studentList.forEach(System.out::println);
sqlSession.close();
}
3.3.6 多个参数-使用Map
? 开发中,并不常用(了解即可)
(1)Dao接口:
public List<Student> selectByMap(Map<String, Object> map);
(2)xml文件:
<select id="selectByMap" resultType="com.st.domain.Student">
select id, name, email, age from student where name=#{myname} or age=#{myage};
</select>
(3)测试代码:
@Test
public void selectByMap() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("myname", "张飞");
map.put("myage",15);
List<Student> studentList = mapper.selectByMap(map);
studentList.forEach(System.out::println);
sqlSession.close();
}
? 存在问题:
-
只能在测试类中,才能给map赋值的问题。 -
可读性差,从Dao中并不能获取需要传入什么信息。
3.3.7 #和$的区别
select id,name, email,age from student where id=#{studentId}
===> #号后的结果: select id,name, email,age from student where id=?
? #使用的是PreparedStatement执行sql
select id,name, email,age from student where id=#{studentId}
===> $号后的结果: select id,name, email,age from student where id=1001
? $使用的Statement对象执行sql, 效率比PreparedStatement低。
?
:
可
以
替
换
表
名
或
者
列
名
,
你
能
确
定
数
据
是
安
全
的
。
可
以
使
用
:可以替换表名或者列名, 你能确定数据是安全的。可以使用
:可以替换表名或者列名,你能确定数据是安全的。可以使用
#和 $区别总结:
- #使用 ?在sql语句中做占位符, 使用PreparedStatement执行sql,效率高
- #能够避免sql注入,更安全。
- $不使用占位符,是字符串连接方式,使用Statement对象执行sql,效率低
- $有sql注入的风险,缺乏安全性。
- $:可以替换表名或者列名
3.4 封装MyBatis输出结果
3.4.1 resultType
? mybatis执行了sql语句,得到ResultSet转换的类型。
处理方式:
- mybatis执行sql语句, 然后mybatis调用类的无参数构造方法,创建对象。
- mybatis把ResultSet列中的值赋给类中同名的属性
resultType结果类型的值可以是:
- 类型的全限定名称
- 类型的别名, 例如 java.lang.Integer别名是int
注意:
- 如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。
- resultType可以返回不是一个实体类的对象, 但返回值需要和xml中resultType一样
①新建一个ViewStudent类:
@Data
@ToString
public class ViewStudent {
private String name;
private Integer age;
}
②向Dao中添加方法:
ViewStudent selectStudetnReturnViewStudent(@Param("sid") Integer id);
③xml文件
<select id="selectStudetnReturnViewStudent" resultType="com.st.vo.ViewStudent">
select id, name, email, age from student where id=#{sid};
</select>
④测试代码
@Test
public void selectStudetnReturnViewStudent() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
ViewStudent student = mapper.selectStudetnReturnViewStudent(1002);
System.out.println(student.toString());
sqlSession.close();
}
⑤运行结果:这个类中包括的name 、age属性得到赋值
? **别名:**全限定名可以使用mybatis自带的别名代替,或者自定义别名
1)使用MyBatis自带别名,举例:
(1)StudentDao接口中加入方法
public List<Student> selectResultByMyBatis(@Param("myname") String name,
@Param("myage") Integer age);
(2)StudentDao.xml文件中加入对应的配置
使用全限定名:
<select id="selectResultByMyBatis" resultType="java.lang.Integer">
select count(*) from student;
</select>
使用自带别名:
<select id="selectResultByMyBatis" resultType="int">
select count(*) from student;
</select>
2)使用自定义别名第一种方式:
在主配置文件中定义,使用定义别名
举例:
(1)主配置文件加入:
<typeAliases>
<typeAlias type="java.lang.Integer" alias="ThisAlias"/>
</typeAliases>
(2)在dao的xml文件中修改
<select id="selectResultByMyBatis" resultType="ThisAlias">
select count(*) from student;
</select>
3)使用自定义别名第二种方式:
? name是包名,这个包中的所有类,类名就是别名(别名不区分大小写)
举例:
(1)主配置文件加入:
<typeAliases>
<package name="java.lang"/>
</typeAliases>
(2)在dao的xml文件中修改
<select id="selectResultByMyBatis" resultType="Integer">
select count(*) from student;
</select>
? 但还是建议不要用第二种别名,用全限定名更好,因为不同的包下的同一名字的类,会有歧义性。
3.4.2 resultMap
? 结果映射,指定列名和java对象的属性对应关系。
resultMap使用方式:
? 1)先定义resultMap
? 2)在select标签,使用resultMap来引用1定义的
resultMap使用场景:
- 自定义列值赋值给指定的属性
- 当列名和属性名不一样时,一定要使用resultMap
举例 场景1:
(1)Dao中添加方法:
public List<Student> selectAllStudent();
(2)xml方法: 通过标签定义resultMap,将name 和 email互换
<resultMap id="studentMap" type="com.st.domain.Student">
<id column="id" property="id"/>
<result column="name" property="email"/>
<result column="email" property="name"/>
<result column="age" property="age"/>
</resultMap>
<select id="selectAllStudent" resultMap="studentMap">
select id, name, email, age from student
</select>
(3)测试代码:
@Test
public void selectAllStudent() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
List<Student> studentList = mapper.selectAllStudent();
studentList.forEach(System.out::println);
sqlSession.close();
}
(4)运行结果:name 和 email完成互换
举例 场景2:当列名和属性名不一样时,一定要使用resultMap
(1)在domain中新建一个MyStudent类,属性和Student相同,但是属性名不同。制造出列名和属性名不一样的场景
@Data
public class MyStudent {
private Integer stuid;
private String stuname;
private String stuemail;
private Integer stuage;
@Override
public String toString() {
return "MyStudent{" +
"stuid=" + stuid +
", stuname='" + stuname + '\'' +
", stuemail='" + stuemail + '\'' +
", stuage=" + stuage +
'}';
}
}
(2)xml方法: 通过标签定义方法对应赋值
<resultMap id="myStudentMap" type="com.st.domain.MyStudent">
<id column="id" property="stuid"/>
<result column="name" property="stuname"/>
<result column="email" property="stuemail"/>
<result column="age" property="stuage"/>
</resultMap>
<select id="selectMyStudent" resultMap="myStudentMap">
select id, name, email, age from student
</select>
(3)测试代码:
@Test
public void selectMyStudent() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
List<MyStudent> myStudents = mapper.selectMyStudent();
myStudents.forEach(System.out::println);
sqlSession.close();
}
(4)运行结果:
3.5 模糊查询like的使用
3.5.1 模糊查询第一种方式
? 在java代码中,执行like的内容, 将like的内容通过参数传递
(1)Dao接口:
List<Student> selectLinkeOne(String name);
(2)xml文件:
<select id="selectLinkeOne" resultType="com.st.domain.Student">
select id, name, email, age from student where name like #{name}
</select>
(3)测试代码:
@Test
public void selectLinkeOne() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
String name = "%张%";
List<Student> studentList = mapper.selectLinkeOne(name);
studentList.forEach(System.out::println);
sqlSession.close();
}
(4)运行结果:name中含有 张 的行记录被找到
3.5.2 模糊查询第二种方法
在mapper 文件中拼接 like的内容
(1)Dao接口:
List<Student> selectLinkeTwo(String name);
(2)xml代码:
<select id="selectLinkeTwo" resultType="com.st.domain.Student">
select id, name, email, age from student where name like "%" #{name} "%" ;
</select>
(3)测试代码:
@Test
public void selectLinkeTwo() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
String name = "剑";
List<Student> studentList = mapper.selectLinkeTwo(name);
studentList.forEach(System.out::println);
sqlSession.close();
}
第四章 动态SQL
? 动态sql: sql的内容是变化的,可以根据条件获取到不同的sql语句,主要是where部分发生变化。
? 动态sql的实现:使用的是mybatis提供的标签, ,,
4.1 if标签
是判断条件的, 语法 部分sql语句
举例:
(1)Dao接口:
List<Student> selectStudentIf(Student student);
(2)xml代码:
<select id="selectStudentIf" resultType="com.st.domain.Student">
<include refid="studentSql"></include>
where 1 = 1
<if test="name != null and name != ''">
and name = #{name}
</if>
<if test="age > 0">
or age > #{age}
</if>
</select>
(3)测试代码:
@Test
public void selectStudentIf() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
Student student1 = new Student();
student1.setName("张飞");
student1.setAge(15);
List<Student> students = mapper.selectStudentIf(student1);
students.forEach(student -> System.out.println(student));
sqlSession.close();
}
(4)运行结果,满足条件的被查找到
4.2 where标签
用来包含 多个的, 当多个if有一个成立的, 会自动增加一个where关键字, 并去掉 if中多余的 and ,or等。
? 语法 …
举例:
(1)Dao接口:
List<Student> selectStudentWhere(Student student);
(2)xml文件:
<select id="selectStudentWhere" resultType="com.st.domain.Student">
select id, name, email, age from student
<where>
<if test="name != null and name != ''">
and name = #{name}
</if>
<if test="age > 0">
or age > #{age}
</if>
</where>
</select>
(3)测试代码:
@Test
public void selectStudentWhere() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
Student student1 = new Student();
student1.setName("张飞");
List<Student> students = mapper.selectStudentWhere(student1);
students.forEach(student -> System.out.println(student));
sqlSession.close();
}
(4)运行结果:
4.3 foreach标签
循环java中的数组,list集合的。 主要用在sql的in语句中。 学生id是 1001,1002,1003的三个学生
select * from student where id in (1001,1002,1003)
public List<Student> selectFor(List<Integer> idlist)
List<Integer> list = new ...
list.add(1001);
list.add(1002);
list.add(1003);
dao.selectFor(list)
<foreach collection="" item="" open="" close="" separator="">
#{xxx}
</foreach>
- collection:表示接口中的方法参数的类型, 如果是数组使用array , 如果是list集合使用list
- item:自定义的,表示数组和集合成员的变量
- open:循环开始是的字符
- close:循环结束时的字符
- separator:集合成员之间的分隔符
举例1:传入的是id,Integer类
(1)Dao接口:
public List<Student> selectForeach(List<Integer> idList);
(2)xml代码:
<select id="selectForeach" resultType="com.st.domain.Student">
select id, name, email, age from student where id in
<foreach collection="list" item="myid" open="(" close=")" separator=",">
#{myid}
</foreach>
</select>
(3)测试代码:
@Test
public void selectForeach() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
List<Integer> list = new ArrayList<>();
list.add(1001);
list.add(1002);
list.add(1003);
List<Student> students = mapper.selectForeach(list);
students.forEach(student -> System.out.println(student));
sqlSession.close();
}
(4)运行结果:
举例2:传入的是类,Student类,用类中的id
(1)Dao接口:
public List<Student> selectForeach2(List<Student> stuList);
(2)xml代码:
<select id="selectForeach2" resultType="com.st.domain.Student">
select id, name, email, age from student where id in
<foreach collection="list" item="stu" open="(" close=")" separator=",">
#{stu.id}
</foreach>
</select>
(3)测试代码:
@Test
public void selectForeach2() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
List<Student> list = new ArrayList<>();
Student student1 = new Student();
Student student2 = new Student();
student1.setId(1003);
student2.setId(1004);
list.add(student1);
list.add(student2);
List<Student> students = mapper.selectForeach2(list);
students.forEach(student -> System.out.println(student));
sqlSession.close();
}
(4)运行结果:
4.4 sql代码片段
? sql代码片段, 就是复用一些代码 步骤:
- 先定义 sql语句, 表名,字段等
- 再使用,
举例:
(1)在xml文件中标签里面,添加:
<sql id="studentSql">
select id, name, email, age from student
</sql>
(2)标签里面语句,即可服用这些代码,以selectStudentIf()为例:
<select id="selectStudentIf" resultType="com.st.domain.Student">
<include refid="studentSql"></include>
where 1 =1
<if test="name != null and name != ''">
and name = #{name}
</if>
<if test="age > 0">
or age > #{age}
</if>
</select>
第五章 MyBatis 配置文件
5.1 数据库的属性配置文件
数据库的属性配置文件: 把数据库连接信息放到一个单独的文件中。 和mybatis主配置文件分开。 目的是便于修改,保存,处理多个数据库的信息。
1)在resources目录中定义一个属性配置文件, xxxx.properties ,例如 jdbc.properties 在属性配置文件中, 定义数据,格式是 key=value key: 一般使用 “.” 做多级目录的。 例如: jdbc.mysql.driver , jdbc.driver, mydriver
? jdbc.driver=com.mysql.jdbc.Driver ? jdbc.url=jdbc:mysql//… ? jdbc.username=root ? jdbc.password=123456
2)在mybatis的主配置文件,使用 指定文件的位置 在需要使用值的地方, ${key}
5.2 主配置文件的一些标签
? 看一份主配置文件都有什么
<?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 resource="jdbc.properties"/>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<package name="java.lang"/>
</typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
<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>
<mapper resource="com/st/dao/StudentDao.xml"/>
</mappers>
</configuration>
- xml文件,需要在头部使用约束文件
- 根元素
- 文件主要包含内容:
5.2.1 transactionManager
MyBatis提交事务 回滚事务的方式
? type:事务的处理类型
? 1)JDBC:表示mybatis底层使用jdbc中Connection对象 commit rollback
? 2)MANAGED:把mybatis事务处理委托给其他的容器 (一个服务器软件 一个框架spring)
5.2.2 dataSource
dataSource:表示数据源,Mybatis 中访问数据库,可以连接池技术,但它采用的是自己的连接池技术。在 Mybatis 的 mybatis.xml 配置文件中,通过来实现 Mybatis 中连接池的配置。
? type:指定数据源的类型
? 1)POOLED:使用连接池,mybatis会创建PooledDataSource类
? 2)UPOOLED:不适用连接池,在每次执行sql语句,先创建连接,执行sql,再关闭连接
? mybatis会创建一个UnpooledDataSource来管理连接池
? 3)JNDI:java命名和目录服务(Windows注册表)
5.2.3 typeAliases 别名
Mybatis 支持默认别名,我们也可以采用自定义别名方式来开发,主要使用在<select resultType="别名"> mybatis.xml 主配置文件定义别名
<typeAliases>
<typeAlias type="java.lang.Integer" alias="ThisAlias"/>
<package name="java.lang"/>
</typeAliases>
5.3 指定多个mapper
第一种方式:
? 写多个
第二种方式:使用包名
? name : xml文件所在的包名
注意:
-
mapper文件名称需要和接口名称一样,区分大小写 -
mapper文件和dao接口需要在同一个目录
5.4 注意事项 !! 在xml写sql语句时候,没有结束的语句,别添加分号 不然可能会出错。
第六章 PageHelper
? PageHelper是一个通用的分页插件,支持多种数据库
-
Oracle -
Mysql -
MariaDB -
SQLite -
Hsqldb -
PostgreSQL -
DB2 -
SqlServer(2005,2008) -
Informix -
H2 -
SqlServer2012 -
Derby -
Phoenix
6.1 使用步骤
使用PageHelper实现分页步骤
(1)pom文件中,加入Maven坐标
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.0</version>
</dependency>
(2)主配置文件中,加入插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
(3)调用PageHelper方法
@Test
public void selectAllByPageHelper() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
PageHelper.startPage(1, 3);
List<Student> students = mapper.selectAllByPageHelper();
students.forEach(student -> System.out.println(student));
sqlSession.close();
}
(4)运行结果,结果只显示前3条信息
|