MyBatis
文章目录
请点击这里 获取本文代码
第一章 框架概述
1.三层架构
MVC基本概念
web开发中,使用mvc架构模式. Model:数据, View:视图, Controller:控制器.
- Controller控制器:: 接收请求,调用service对象, 显示请求的处理结果.当前使用servlet作为控制器
- View视图: 现在使用jsp,html,css,js.显示请求的处理结果,把m中的数据显示出来.
- Model数据:来自mysql,来自文件,来自网络.
MVC作用
- 实现解耦合.
- 让mvc各司其职.每个模块只负责各自的功能
- 使得系统的扩展性更好,更容易维护
三层架构
- 界面层(视图层):接受用户的请求,调用service,显示请求的处理结果的.包含jsp,html,servlet等对象. 对应的包controller.
- 业务逻辑层: 处理业务逻辑,使用算法处理数据的.把数据返回给界面层.对应的是service包,包中很多的XXXService类.例如: StudentService, OrderService,ShopService
- 持久层(数据库访问层): 访问数据库, 或者读取文件, 访问网络,获取数据.对应的包是dao. 例如:StudentDao, OrderDao, ShopDao等等.
2.三层架构请求的处理流程
3.为什么要使用三层?
- 结构清晰,耦合度低,各层分工明确
- 可维护性高,可扩展性高
- 有利于标准化
- 开发人员可以值关注整体结构中的其中一层的功能实现
- 有利于于各层逻辑的复用
4.三层架构模式和框架
每一层对应着一个框架
- 界面层—SpringMVC框架
- SpringMVC数据SpringFrameWork 3.0版本加入的一个模板,为Spring框架提供了构建web应用程序的能力.
- 业务层—Spring框架
- Spring框架是为了解决软件开发的复杂性而创建的.Spring使用的是基本的JavaBean来完成以前非常复杂的企业级开发.
- 持久层—MyBatis框架
- mybatis是一个优秀的基于java的持久层框架,内部封装了jdbc,开发者只需要关注sql本身,而不需要处理加载驱动, 创建连接, 创建statement,关闭连接.
5.框架
1.什么是框架(framework)
框架:就是一个软件,完成了部分的功能.软件中的类与类之间的发发调用都已经规定好了.通过这些可以完成某些功能.框架可以看作是模板.
框架是可以升级和改造的.框架是安全的.
框架是对某一方面有用的,不是全能的.需要结合其他的框架一起使用.
6.框架能解决的问题
- 框架能实现技术的整合.
- 框架能够提高开发的效率,降低难度.
7.jdbc访问数据库的优缺点
优点:
- 直观,容易理解
缺点:
- 要创建很多对象 Connetcion,Statment,ResultSet
- 注册驱动
- 执行sql语句
- 把ResultSet转为对象,List集合.
- 关闭资源
- sql语句和逻辑代码混在一起
8.MyBatis框架
1.基本概述
mybatis是一个持久层框架,可以操作数据库,操作数据库的增删改查.可以看作是高级的jdcb,解决jdcb的缺点. 它本是Apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了 google code, 并改名为MyBatis. 2013年11月迁移到github.
2.Mybatis解决的主要问题
- 注册驱动
- 创建jdbc中使用的Connection,Statement,ResultSet对象.
- 执行sql语句,得到ResultSet对象
- 处理ResultSet, 把记录集中的数据转为java对象, 同时还能把java对象放入到集合.
- 关闭资源.
- 实现sql语句和java代码解耦合.(mybatis中,将sql语句放到xml文件)
3.Mybatis的官网
官网: https://mybatis.org/mybatis-3/zh/getting-started.html
maven:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
第二章 MyBatis入门
2.1 第一个例子
实现步骤:
- 创建student表(id,name,email,age)
- 新建maven项目
- 修改pom.xml
-
加入依赖mybatis依赖, mysql驱动,单元测试junit
<?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.nguyenxb</groupId> <artifactId>ch01-first</artifactId> <version>1.0-SNAPSHOT</version> <!--jdk版本--> <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.11</version> <scope>test</scope> </dependency> <!-- mybatis依赖--> <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>5.1.9</version> </dependency> </dependencies> <!-- 资源插件 在build中配置resources,来防止我们资源导出失败的问题--> <build> <resources> <resource> <!-- 在src/main/resources 目录下可以包含properties、xml文件--> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build> </project>
-
在
<build>
中加入资源插件
-
- 创建实体类Student.定义属性,属性名和列名保持一致
- 创建Dao接口,定义操作数据库的方法
- 创建xml文件(mapper文件),写sql语句.
- mybatis框架推荐是把sql语句和java代码分开
- mapper文件:定义和dao接口在同一目录,一个表一个mapper文件
- 创建mabatis的主配置文件(xml文件):有一个,放在resources目录下.
- 定义创建连接示例的数据源(DataSoure)对象
- 指定其他mapper文件的位置
- 创建测试内容.
- 使用main方法,测试mybatis访问数据
- 也可以使用junit访问数据库
配置文件详解
文件目录
mybatis.xml
文件
mybatis主配置文件: mybatis.xml
, 存放路径:src\main\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">
<!-- 1.约束文件: 可以打开看的
http://mybatis.org/dtd/mybatis-3-config.dtd
约束文件的作用:定义和限制当前文件中可以使用的标签和属性,以及标签出现的顺序-->
<configuration>
<settings>
<!-- 配置日志 : name 的值是固定的,
value 是 STDOUT_LOGGING : 直接将mybatis操作数据库的日志输出到控制台 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!-- 配置数据源: 创建Connection对象-->
<dataSource type="POOLED">
<!-- driver:驱动的内容-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!-- 连接数据库的url: 不能直接使用 & 符号,要将其转换为 html 实体即 & 代表的就是 & 符号 -->
<property name="url" value="jdbc:mysql://localhost:3306/ssm?useSSL=true&useUnicode=true&characterEncoding=utf-8"/>
<!-- 用户名-->
<property name="username" value="root"/>
<!-- 用户密码-->
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 指定其他mapper文件的位置:
目的: 使用这里的连接数据库信息来执行其他文件的sql语句
-->
<mappers>
<!--
使用mapper的resource属性指定mapper文件的路径.
这个路径是从target/classes路径开始的
使用注意:
resource = "mapper文件的路径,使用 / 分割路径"
一个mapper resource 指定一个mapper文件
-->
<mapper resource="com\nguyenxb\dao\StudentDao.xml"/>
</mappers>
</configuration>
StudentDao.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">
<!--
1.约束文件: 可以打开看的
http://mybatis.org/dtd/mybatis-3-mapper.dtd
约束文件的作用:定义和限制当前文件中可以使用的标签和属性,以及标签出现的顺序
2.mapper是根标签
namespace: 命名空间,必须有值,不能为空.唯一值.
推荐使用Dao接口的全限定名称.
作用: 参与识别sql语句的作用.
3.在mapper里面可以写<insert>,<update>,<delete>,<select>等标签.
<insert>里面是 insert语句, 表示执行 insert操作
<update>里面是 update语句,
<delete>里面是 delete语句,
<select>里面是 select语句,
这些标签对于出现的顺序没有限制,并且可以出现多次
-->
<mapper namespace="com.nguyenxb.dao.StudentDao">
<!-- 查询一个学生的Student
<select> : 表示查询操作,里面是select 语句
id : 要执行的sql语句的唯一标识,是一个自定义字符串
推荐使用dao接口中的方法名称
resultType:告诉mybatis,执行sql语句,把数据赋值给哪个类型的java对象.
resultType的值现在使用的java对象的全限定名称
全限定名称: 就是mybatis通过反射机制创建resultType中的对象,并且将语句中的同名字段
赋值给新创建的对象.然后mybatis再将数据返回
#{studentId} 就是 占位符
-->
<select id="selectStudentById" resultType="com.nguyenxb.entity.Student">
select id,name,email,age from student where id = #{studentId}
</select>
</mapper>
Student.java
学生实体类
package com.nguyenxb.entity;
public class Student {
// 属性和列名一样
private Integer id;
private String name;
private String email;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", age=" + age +
'}';
}
}
StudentDao.java
学生查询数据库的接口
package com.nguyenxb.dao;
import com.nguyenxb.entity.Student;
public interface StudentDao {
// 查询一个学生
Student selectStudentById(Integer id);
}
MyTest.java
package com.nguyenxb;
import com.nguyenxb.entity.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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class MyTest {
// 类名不要Test,否则与注解可能会发生冲突
// 测试mybatis执行sql语句
@Test
public void testSelectStudentById() throws IOException {
// 调用mybatis某个对象的方法,执行mapper文件中的sql语句
// mybatis 核心类 : SqlSessionFactory
// 1 定义mybatis主配置文件的位置,从类路径开始的相对路径
String config = "mybatis.xml";
// 2 读取主配置文件,使用mybatis框架中的Resources类
InputStream inputStream = Resources.getResourceAsStream(config);
// 3 创建SqlSessionFactory对象, 使用SqlSessionFactoryBuilder对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
// 4 获取 SqlSession对象
SqlSession sqlSession = factory.openSession();
// 5 指定要执行的sql语句的 id
// sql的id = mapper的 namespace+"."+<select>|update|insert|delete标签的id属性
String sqlId = "com.nguyenxb.dao.StudentDao"+"."+"selectStudentById";
// 6 通过sqlSession 执行sql语句
Student student = (Student)sqlSession.selectOne(sqlId,1001);
System.out.println("使用mybatis查询:"+student.toString());
// 7 关闭资源
sqlSession.close();
}
}
运行结果详解:
2.2 概念
基本概念
- 自动提交 : 当你的sql语句执行完毕后, 立即提交事务. 数据库更新操作直接保存到数据库.
- 手动 (手工) 提交: 在你需要提交事务的位置,执行方法, 提交事务或者回滚事务.mybatis默认手工提交事务, 在执行 insert, update, delete 语句时需要提交事务
示例:
注 : 如果无法执行代码,请往前查看其他相关的配置
在StudentDao.xml中添加下面的操作数据库的语句以及对应的标签
<mapper namespace="com.nguyenxb.dao.StudentDao">
<!-- 添加 insert
insert into student values(1003,"哈哈","haha@qq.ocm",20)
如果传入给 mybatis 是一个java对象, 使用#{属性名} 获取次属性的值.
属性值放到 #{} 占位符的位置, mybatis 执行此属性,对应的getXxx().
例如 #{id} , 执行 getId();
-->
<insert id="insertStudent">
insert into student values(#{id},#{name},#{email},#{age})
</insert>
</mapper>
将此段代码放到一个类中,并导入junit
的jar包即可运行
@Test
public void testInsertStudent() throws IOException {
// 定义mybatis主配置文件的位置
String config = "mybatis.xml";
// 读取配置文件
InputStream inputStream = Resources.getResourceAsStream(config);
// 使用 sqlSessionFactoryBuilder的build方法 创建sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取 sqlSession 对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建 sql id ,插入数据
String sqlId = "com.nguyenxb.dao.StudentDao"+"."+"insertStudent";
// 创建要插入数据的对象
Student student = new Student();
student.setId(1004);
student.setName("阿瑟东");
student.setEmail("asd@qq.com");
student.setAge(23);
// 执行插入操作
int rows = sqlSession.insert(sqlId,student);
// 提交事务
sqlSession.commit();
System.out.println("使用mybatis添加一个学生,rows:"+rows);
sqlSession.close();
}
执行成功后,数据库将直接插入一个学生对象
解决MyBatis找不到文件问题(idea版本)
-
首先检查
mybatis.xml
中的映射路径,是否正确配置. -
检查
XxxDao.xml
中的是否加入相关的sql语句映射, 或者 java代码中的namespace与sql标签对应的id是否一致 (注意 : namespace 与 sql标签的id之间 有一个.
连接) -
在pom.xml中加入资源插件
<!-- 资源插件 在build中配置resources,来防止我们资源导出失败的问题--> <build> <resources> <resource> <!-- 在src/main/resources 目录下可以包含properties、xml文件--> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>
-
重新build工程
-
打开工程目录, 点击pom.xml, 右键 ,点Maven, 点击Reload project
-
重新加载maven
-
重新启动idea
-
手动复制文件
-
重新创建一个新的项目
2.3 MyBatis对象分析
对象
Resource , SqlSession,SqlSessionFactory等
1 Resource对象
Resource类, 顾名思义就是资源, 用于读取资源文件.其实有很多方法可以通过加载并解析资源文件,返回不同类型的IO,对象.
定义mybatis主配置文件的位置,从类路径开始的相对路径
String config = "mybatis.xml";
读取主配置文件,使用mybatis框架中的Resources类
InputStream inputStream = Resources.getResourceAsStream(config);
2 SqlSessionFactory对象与SqlSessionFactoryBuilder对象
- 获取SqlSessionFactory对象,重要对象, 他是一个 重量级对象: 创建此对象需要使用更多的资源和时间.在项目中有一个就可以了.
- SqlSessionFactory接口: 作用是sqlSession的工厂, 就是创建SqlSession对象.
- DefaultSqlSessionFactory实现类
public class DefaultSqlSessionFactory implements SqlSessionFactory {}
SqlSessionFactory接口中常用的方法:
// 获取一个默认的SqlSession对象,默认是需要手工提交事务的
SqlSession openSession();
// boolean 参数表示是否自动提交事务,
/// true : 创建一个自动提交事务的SqlSession
// false : 等同于没有参数的openSession
SqlSession openSession(boolean var1);
可以使用使用SqlSessionFactoryBuilder对象 来创建使用SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
3 SqlSession对象
- 获取 SqlSession对象, 通过SqlSessionFactory对象的openSession方法
SqlSession sqlSession = factory.openSession();
- SqlSession对象是通过SqlSessionFactory获取的, SqlSession本身是接口
实现类:DefaultSqlSession, 是线程不安全的
public class DefaultSqlSession implements SqlSession {}
SqlSession对象是提供了大量的执行sql语句的方法:
selectOne: 执行sql语句,最多只能返回1行记录,多于1行就会报错
selectList: 执行sql语句,返回多行数据
selectMap: 执行sql语句,返回一个Map结果
insert:执行insert语句
update: 执行update语句
delete: 执行delete语句
commit: 提交事务
rollback:回滚事务
注意: SqlSession对象是线程不安全的,因此使用步骤如下:
- 在方法内部,执行sql语句之前,先获取SqlSession对象
- 调用SqlSession的方法,执行sql语句
- 关闭SqlSession对象,执行SqlSession.close()
<mapper namespace="com.nguyenxb.dao.StudentDao">
<!-- 查询一个学生的Student
<select> : 表示查询操作,里面是select 语句
id : 要执行的sql语句的唯一标识,是一个自定义字符串
推荐使用dao接口中的方法名称
resultType:告诉mybatis,执行sql语句,把数据赋值给哪个类型的java对象.
resultType的值现在使用的java对象的全限定名称
全限定名称: 就是mybatis通过反射机制创建resultType中的对象,并且将语句中的同名字段
赋值给新创建的对象.然后mybatis再将数据返回
-->
<select id="selectStudentById" resultType="com.nguyenxb.entity.Student">
select id,name,email,age from student where id = #{studentId}
</select>
</mapper>
- 指定要执行的sql语句的 id , sqlId = mapper中的 namespace+"."+
select|update|insert|delete
标签的id属性
String sqlId = "com.nguyenxb.dao.StudentDao"+"."+"selectStudentById";
- 通过sqlSession 执行sql语句
Student student = (Student)sqlSession.selectOne(sqlId,1001);
- 关闭资源
sqlSession.close();
2.4 mybatis基本用法
mybatis工具类
MyBatisUtil.java
package com.nguyenxb.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;
/**
* 工具类 : 获取 MyBatis的SqlSession对象
*/
public class MyBatisUtil {
private static SqlSessionFactory factory = null;
static {
String config = "mybatis.xml";
try {
InputStream inputStream = Resources.getResourceAsStream(config);
factory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @param autoCommitFlag true:自动提交事务 , false: 手动提交事务
* @return SqlSession对象
*/
public static SqlSession getSqlSession(boolean autoCommitFlag){
SqlSession sqlSession = null;
if (factory != null){
if (autoCommitFlag){
sqlSession = factory.openSession(true);
}else {
sqlSession = factory.openSession();
}
}
return sqlSession;
}
/**
* @return 手工提交事务的SqlSession对象
*/
public static SqlSession getSqlSession(){
SqlSession sqlSession = null;
if (factory != null){
sqlSession = factory.openSession();
}
return sqlSession;
}
}
mapper文件
StudentDao.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="com.nguyenxb.dao.StudentDao">
<!--Enter your sql statement with (select | insert | update |delete) label-->
<select id="selectStudentById" resultType="com.nguyenxb.entity.Student">
select id,name,email,age from student where id = #{studentId}
</select>
<select id="selectStudents" resultType="com.nguyenxb.entity.Student">
select * from student
</select>
<select id="insertStudent">
insert into student values(#{id},#{name},#{email},#{age})
</select>
</mapper>
XxxDao接口实现类
例: 学生Dao接口实现类 StudentDaoImpl.java
package com.nguyenxb.dao.Impl;
import com.nguyenxb.dao.StudentDao;
import com.nguyenxb.entity.Student;
import com.nguyenxb.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class StudentDaoImpl implements StudentDao {
@Override
public Student selectStudentById(Integer id) {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
String sqlId = "com.nguyenxb.dao.StudentDao.selectStudentById";
Student student = sqlSession.selectOne(sqlId, id);
sqlSession.close();
return student;
}
@Override
public List<Student> selectStudents() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
String sqlId = "com.nguyenxb.dao.StudentDao.selectStudents";
List<Student> students = sqlSession.selectList(sqlId);
sqlSession.close();
return students;
}
@Override
public int insertStudent(Student student) {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
String sqlId = "com.nguyenxb.dao.StudentDao.insertStudent";
int rows = sqlSession.insert(sqlId, student);
sqlSession.commit();
sqlSession.close();
return rows;
}
}
Test类
对 StudentDaoImpl类进行测试
package com.nguyenxb.dao;
import com.nguyenxb.dao.Impl.StudentDaoImpl;
import com.nguyenxb.entity.Student;
import org.junit.Test;
import java.util.List;
public class StudentDaoTest2 {
@Test
public void testSelectStudentById(){
StudentDaoImpl dao = new StudentDaoImpl();
Student student = dao.selectStudentById(1001);
System.out.println(student.toString());
}
@Test
public void testSelectStudents(){
StudentDaoImpl dao = new StudentDaoImpl();
List<Student> students = dao.selectStudents();
// lambda 表达式
students.forEach(student -> System.out.println(student.toString()));
}
@Test
public void testInsertStudent(){
StudentDaoImpl dao = new StudentDaoImpl();
Student student = new Student();
student.setId(1007);
student.setName("阿萨");
student.setEmail("as@qq.com");
student.setAge(30);
int rows = dao.insertStudent(student);
System.out.println(rows);
}
}
第三章 MyBatis的Dao代理
3.1 dao代理
基本概念
mybatis提供代理: mybatis创建Dao接口实现类对象,完成对sql语句的执行.
下面通过StudentDaoImpl中的selectStudentById() 方法来了解MyBatis的Dao代理
/*
String sqlId = "com.nguyenxb.dao.StudentDao.selectStudentById";
Student student = sqlSession.selectOne(sqlId, id);
测试方法中: 调用dao的方法
Student student = dao.selectStudentById(1001);
mybatis 操作数据库的必要信息:
(1) dao: 通过反射可以得到 全限定类型名称 ,dao是StudentDao 类型的, 其全限定名称 com.nguyenxb.dao.StudentDao
(2) selectStudentById : dao中的方法名称, 方法名称就是 mapper 文件中标签的id, 通过dao.selectStudentById() 能得到 sqlId = "com.nguyenxb.dao.StudentDao.selectStudentById";
(3) 如何确定调用SqlSession的哪个方法?
1.根据dao接口的方法返回值,如果返回的是一个对象,例如Student,调用的是SqlSession.selectOne()
如果dao 接口中的方法返回List,调用SqlSession的selectList();
2.根据mapper 文件中的标签,如果标签是<insert> , 调用SqlSession.insert()方法
* */
因此 在mybatis框架中,由于发现使用dao的方法调用就能确定执行sql语句的必要信息,所以mybatis简化了dao对象的实现.
MyBatis的代理技术 (动态代理, dao的动态代理) : 由mybatis 框架在程序执行期间,根据你的Dao接口, 创建一个内存中的接口实现类对象.
举例分析: 由mybatis创建StudentDao接口的实现类 StudentDaoImpl,
使用框架创建的StudentDaoImpl 来代替手工实现的StudentDaoImpl 类的功能,
就不需要开发人员写dao接口的实现类了
实际情况 : mybatis 实际上的创建XxxDao 接口的实现类是 Proxy 类, mybatis在内部通过反射机制实现了 XxxDaoImpl.
使用 MyBatis的 dao 的代理要求:
- mapper文件中的namespace : 必须使用dao接口的全限定名称 (namespace+标签id)
- mapper文件中标签的id 必须与 dao 接口中的方法名称一样
Mybatis代理的实现方式
使用SqlSession对象的方法 getMapper(XxxDao.class)
例如 : 现在有StudentDao接口, 使用代理的方式: 即不需要人工实现StudentDao接口
// 获取 sqlSession操作数据
SqlSession sqlSession = MyBatisUtil.getSqlSession();
// 使用代理模式
StudentDao studentDao=sqlSession.getMapper(StudentDao.class);
// 查询学生信息
Student student = studentDao.selectStudentById(1001);
System.out.println(student.toString());
sqlSession.close();
// 上面代码中
StudentDao studentDao=sqlSession.getMapper(StudentDao.class);
// 等同于
// 传统方式:即需要实现StudentDao接口, 即需要创建StudentDaoImpl类
StudentDaoImpl dao = new StudentDaoImpl();
3.2 理解参数
理解参数是: 通过java程序把数据传入到mapper文件中的sql语句. 参数主要是指dao接口的形参.
parameterType
parameterType: 表示参数的类型, 指dao方法的形参数据类型.这个形参的数据类型是给mybatis使用的.mybatis在给sql语句的参数赋值时使用,如 PreparedStatement.setXxx(位置, 值).
dao接口方法传入一个简单参数
Dao接口中方法的参数只是一个简单类型(java基本类型和String), 占位符使用 #{任意字符}
, 与方法的参数名无关.
例1
dao接口定义
// 根据id 查询一个学生
Student selectStudentById(Integer id);
dao接口使用的是简单类型的参数,mapper文件中要获取这个参数值,使用#{任意字符}
mapper文件
<select id="selectById" parameterType="java.lang.Integer" resultType="com.nguyenxb.entity.Student">
select id,name,email,age from student where id = #{studentId}
</select>
测试方法
@Test
public void testSelectStudentById(){
// 获取 sqlSession操作数据
SqlSession sqlSession = MyBatisUtil.getSqlSession();
// 使用代理模式
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = studentDao.selectStudentById(1001);
System.out.println(student.toString());
sqlSession.close();
}
例2
dao接口定义
// 根据email 查询一个学生
Student selectByEmail(String email);
dao接口使用的是简单类型的参数,mapper文件中要获取这个参数值,使用#{任意字符}
mapper文件
<select id="selectByEmail" resultType="com.nguyenxb.entity.Student">
select id,name,email,age from student where email=#{studentEmail}
</select>
测试方法
@Test
public void testOneParameter(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
Student student = dao.selectByEmail("as@qq.com");
System.out.println(student.toString());
sqlSession.close();
}
dao接口方法传入多个简单类型的参数
@Param: 命名参数, 在方法的形参前面使用, 定义参数名.这个名称可以用在mapper文件中.
例1
dao接口定义
/*
* 多个简单类型的参数
* 使用 @Param 命名参数, 注解是mybatis提供的
* 位置: 在形参定义的前面
* 属性:value 自定义的参数名称
* 基本语法格式 :
* @Param(value="myname") 参数
* 当然也可以省略value 即 @Param("myname")
*
* */
List<Student> selectByNameOrAge(@Param(value = "myname") String name,
@Param("myage") Integer age);
mapper文件
<!-- 多个简单类型的参数
当使用了@Param 命名后, 例如 @Param(value="myname")
在maper中, 使用#{命名的参数} 例如#{myname}
-->
<select id="selectByNameOrAge" resultType="com.nguyenxb.entity.Student">
select id,name,email,age from student where name=#{myname} or age =#{myage}
</select>
测试方法
@Test
public void testSelectByNameOfAge(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
List<Student> students = dao.selectByNameOrAge("阿迪",23);
students.forEach(student -> System.out.println(student.toString()));
sqlSession.close();
}
dao接口方法传入一个对象作为参数
方法的形参是一个java对象,这个java对象表示多个参数.使用对象的属性值作为参数使用.
例
类定义
public class Student {
private Integer id;
private String name;
private String email;
private Integer age;
// set | get | toString 方法已省略
}
public class QueryParam {
private Object p1;
private Object p2;
// set | get | toString 方法已省略
}
dao接口定义
/*
* 一个java对象作为参数(对应由属性,每个属性有set,get方法)
* */
List<Student> selectByObject(Student student);
List<Student> selectByObject2(QueryParam param);
mapper文件
<!-- 一个java对象作为方法的参数,使用对象的属性作为参数值使用
简单的语法: #{属性名}, mybatis调用此属性的getXxx方法获取属性值
-->
<select id="selectByObject" resultType="com.nguyenxb.entity.Student">
select id,name,email,age from student where name=#{name} or age = #{age}
</select>
<select id="selectByObject2" resultType="com.nguyenxb.entity.Student">
select id,name,email,age from student where name =#{p1} or age=#{p2}
</select>
测试方法
// 例1
@Test
public void testSelectByObject(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
Student student = new Student();
student.setName("阿迪");
student.setAge(15);
List<Student> students = dao.selectByObject(student);
students.forEach(student1 -> System.out.println(student.toString()));
sqlSession.close();
}
// 例2
@Test
public void testSelectByObject2(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
QueryParam queryParam = new QueryParam();
queryParam.setP1("阿瑟东");
queryParam.setP2(15);
List<Student> students = dao.selectByObject2(queryParam);
students.forEach(student1 -> System.out.println(student1.toString()));
sqlSession.close();
}
此外mybatis的sql语句的参数还支持指定数据类型
例如
#{property,javaType=int,jdbcType=NUMERIC}
<!--
property : 传入的具体值
javaType:java接口的传入值为 int类型,
jdbcType: 数据库的字段类型为 NUMERIC类型
-->
接口定义
List<Student> selectByObject(Student student);
mapper文件
<select id="selectByObject" resultType="com.nguyenxb.entity.Student">
select id,name,email,age from student where
name=#{name,javaType=java.lang.String,jdbcType=VARCHAR}
or
age = #{age,javaType=java.lang.Integer,jdbcType=INTEGER}
</select>
dao接口中传入多个简单类型的参数,使用位置
参数位置: dao接口中方法的形参列表,从左往右,参数位置是0,1,2….
语法格式: #{arg0} #{arg1}
dao接口定义
List<Student> selectByPosition(String name,Integer age);
mapper文件
<!-- 使用位置获取参数值, dao接口方法是多个简单类型的参数
语法: #{arg0} #{arg1}....
-->
<select id="selectByPosition" resultType="com.nguyenxb.entity.Student">
select id,name,email,age from student where name = #{arg0} or age = #{arg1}
</select>
测试
@Test
public void testSelectByPosition(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
List<Student> students = dao.selectByPosition("阿瑟东",15);
students.forEach(student1 -> System.out.println(student1.toString()));
sqlSession.close();
}
dao接口中传入map参数
map作为dao接口的参数,在mapper文件中可以使用key获取参数值
dao接口文件
List<Student> selectStudentByMap(Map<String,Object> map);
mapper文件
<!-- 使用map 传递参数
在 mapper 文件中, 获取 map的值, 是通过key获取的,语法: #{key}
-->
<select id="selectStudentByMap" resultType="com.nguyenxb.entity.Student">
select id,name,email,age from student where name = #{myname} or age = #{myage}
</select>
测试
@Test
public void testSelectByMap(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
Map<String,Object> map = new HashMap<>();
map.put("myname","哈哈");
map.put("myage",23);
List<Student> students = dao.selectStudentByMap(map);
students.forEach(student1 -> System.out.println(student1.toString()));
sqlSession.close();
}
3.3 #和$的区别
#
占位符
语法: #{字符}
mybatis处理#{} 使用jdbc对象是PrepareStatment对象
<select id="selectById" parameterType="java.lang.Integer" resultType="com.nguyenxb.entity.Student">
select id,name,email,age from student where id = #{studentId}
</select>
mybatis创建出PrepareStatement对象,执行sql语句
String sql = "select id,name,email,age from student where id=?";
PrepareStatement pst = conn.prepareStatement(sql);
pst.setInt(1,1001);// 传递参数
ResultSet rs = pst.executeQuery();// 执行sql语句
#{}的特点:
- 使用的PrepareStatement对象,执行sql语句,效率高.
- 使用的PrepareStatement对象,能够避免sql语句注入,sql语句执行更安全.
- #{} 常常作为列值使用的,位于等号的右侧,#{}位置的值和数据类型有关.
$
占位符
语法: ${字符}
mybatis执行${占位符}的sql语句
<select id="selectById" parameterType="java.lang.Integer" resultType="com.nguyenxb.entity.Student">
select id,name,email,age from student where id = #{studentId}
</select>
${} 表示字符串连接,把sql语句的其他内容和${}内容使用字符串(+) 连接的方式在一起
String sql = "select id,name,email,age from student where id=" + "1001";
mybatis创建Statement对象,执行sql语句
PrepareStatement pst = conn.createStatement(sql);
pst.setInt(1,1001);// 传递参数
ResultSet rs = pst.executeQuery();// 执行sql语句
${}的特点
- 使用Statement对象,执行sql语句,效率低
- ${}占位符的值,使用的是字符串连接的方式,有sql注入的风险.有代码安全问题.如传入数据
'阿迪' or 1=1
- ${} 数据是原样使用的,不会区分数据类型.
- ${} 常用做表名或者列名,在能保证数据安全的情况下使用.
例如:(这是操作数据的例子,很少用)
dao接口
// ${} 占位符的使用
List<Student> queryStudent(@Param("studentName") String name);
mapper文件
<!-- ${} 的使用-->
<select id="queryStudent" resultType="com.nguyenxb.entity.Student">
select * from student where name = ${studentName}
</select>
测试, 传入的是 '阿迪'
,是有单引号的,并且mybatis不会对其进行任意的数据处理.
@Test
public void testQueryStudent(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
List<Student> students = dao.queryStudent("'阿迪'");
students.forEach(student -> System.out.println(student.toString()));
sqlSession.close();
}
举例2 :
假设传入值为 id
.
${}
一般用途: 用于操作列名,表名, 其对应的sql语句是
select * from student order by id desc
,这个操作的才是列
注意: 在使用#{}是没有效果的, 其对应的sql语句是
select * from student order by 'id' desc
,这个操作的是字符串
dao接口定义
// ${} 占位符的使用: 操作列名,获取降序排序的学生列表
List<Student> queryStudentByColName(@Param("colName") String name);
mapper
<select id="queryStudentByColName" resultType="com.nguyenxb.entity.Student">
select * from student order by ${colName} desc
</select>
测试
@Test
public void testQueryStudentByColName(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
List<Student> students = dao.queryStudentByColName("id");
students.forEach(student -> System.out.println(student.toString()));
sqlSession.close();
}
举例3:
#{}和${}一起使用
dao接口定义
// ${} 占位符的使用: 操作列名
List<Student> queryStudents(
@Param("tableName")String tableName,
@Param("name") String name,
@Param("colName") String colName);
mapper文件
<select id="queryStudents" resultType="com.nguyenxb.entity.Student">
select * from ${tableName} where name = #{name} order by ${colName} desc
</select>
测试 : 查询student表中,名字为阿迪,的所有人, 并且降序排序输出, 其sql 语句如下:
select * from student where name = '阿迪' order by id desc
@Test
public void testQueryStudents(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
List<Student> students = dao.queryStudents("student",
"阿迪","id");
students.forEach(student -> System.out.println(student.toString()));
sqlSession.close();
}
3.4 封装MyBatis的输出结果
封装输出结果: MyBatis执行sql语句,得到ResultSet,转为java对象.
resultType
resultType表示自定义对象
查询数据库返回一个对象
resultType属性: 在执行select时使用,作为<select>
标签的属性出现.
resultType: 表示结果类型, mysql执行sql语句, 得到java对象的类型. 它的值有两种
(1) java类型的全限定名称, (2)使用别名
// dao接口中的方法 : 根据id 查询一个学生
Student selectStudentById(Integer id);
<select id="selectStudentById" resultType="com.nguyenxb.entity.Student">
select id,name,email,age from student where id = #{studentId}
</select>
resultType: 现在使用java类型的全限定名称. 表示的意思是 mybatis执行sql,把ResultSet中的数据转为Student类型的对象.mybatis会做以下操作:
1.调用com.nguyenxb.entity.Student的无参构造方法,创建对象.
Student student = new Student(); // 使用反射创建对象
2.同名的列赋值给同名的属性.
student.setId(rs.getInt("id"));
student.setName(rs.getString("name"));
3.得到java对象,如果dao接口返回值是List集合,mybatis把student对象放入List集合.
所以执行Student mystudent = dao.selectById(1001);// 得到数据库中 id = 1001 这行数据. 并且将这行数据的列值,赋值给mystudent对象的属性.即我们得到的每一个对象的数据,就是数据库中每一行的值.
resultType返回对象的基本规则
当返回对象时,如果对象的属性名和列名一样,则将列中的数据赋值给对象,如果不一样,则不赋值.
类定义
public class CustomObject {
private Integer cid;
private String cname;
private String email;
private Integer age;
// 省略了 set | get |toString 方法
}
dao接口定义
CustomObject selectCustomObjectById(@Param("stuid") Integer id);
mapper文件
<select id="selectCustomObjectById" resultType="com.nguyenxb.pojo.CustomObject">
select id,name,email,age from student where id = #{stuid}
</select>
测试
@Test
public void testSelectCustomObjectById(){
// 获取 sqlSession操作数据
SqlSession sqlSession = MyBatisUtil.getSqlSession();
// 使用代理模式
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
CustomObject customObject = studentDao.selectCustomObjectById(1001);
System.out.println(customObject.toString());
sqlSession.close();
}
// 其结果为 CustomObject{cid=null, cname='null', email='123456@qq.com', age=12}
resultType表示简单类型
查询数据库返回一个值
dao接口定义
// 获取 表中 有行数据
long countStudent();
mapper文件
<!-- 执行sql语句,得到一个值,一行一列-->
<select id="countStudent" resultType="java.lang.Long">
select count(*) from student
</select>
测试
@Test
public void testCountStudent(){
// 获取 sqlSession操作数据
SqlSession sqlSession = MyBatisUtil.getSqlSession();
// 使用代理模式
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
long countStudent = studentDao.countStudent();
System.out.println("行数:"+countStudent);
sqlSession.close();
}
// 结果输出 : 行数: 7
resultType表示一个Map结构
例1: 一个map表示一行数据,dao 接口返回是一个map, sql语句最多只能获取一行记录, 多于1行记录会报错
dao接口定义
// 查询结果返回一个Map结构数据
Map<Object,Object> selectMap(@Param("stuid") Integer id);
mapper文件
<!--
执行结构得到一个Map结构数据, mybatis执行sql,把ResultSet转为mao
sql 执行结构是,列名做map的key,列值做value
sql 执行得到是一行记录,转为map结构是正确的.
-->
<select id="selectMap" resultType="java.util.Map">
select id,name,age from student where id = #{stuid}
</select>
测试
@Test
public void testSelectMap(){
// 获取 sqlSession操作数据
SqlSession sqlSession = MyBatisUtil.getSqlSession();
// 使用代理模式
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Map<Object, Object> map = studentDao.selectMap(1005);
System.out.println("map === "+map);
sqlSession.close();
}
// 结果输出 : map === {name=阿瑟东, id=1005, age=23}
resultMap
resultMap: 结果映射. 自定义列名和java对象属性的对应关系. 常用在列名和属性名不同的情况.
用法:
- 先定义resultMap标签,指定列名和属性名称对应关系
- 在select标签使用resultMap属性,指定上面定义的resultMap的id值
dao接口定义
CustomObject selectCustomObjectById(@Param("stuid") Integer id);
mapper文件
<!-- 使用resultMap定义列和属性的关系-->
<!-- 定义resultMap: 一经定义可以反复使用
id : 给resultMap的映射关系起个名称,唯一值
type:java类型的全限定名称
-->
<resultMap id="customMap" type="com.nguyenxb.pojo.CustomObject">
<!-- 定义列名和属性名的对应关系-->
<!-- 主键类型使用id标签 : 将数据库表的id 赋值给CustomObject类的cid -->
<id column="id" property="cid"/>
<!-- 非主键列使用result标签-->
<result column="name" property="cname"/>
<!-- 列名和属性名相同不用定义,当然定义也没有问题-->
<result column="email" property="email"/>
<result column="age" property="age"/>
</resultMap>
<!-- 使用resultMap属性,指定映射关系的id,注 : resultType和 resultMap只能使用一个-->
<select id="selectCustomObjectById" resultMap="customMap">
select id,name,email,age from student where id = #{stuid}
</select>
测试
public void testSelectCustomObjectById(){
// 获取 sqlSession操作数据
SqlSession sqlSession = MyBatisUtil.getSqlSession();
// 使用代理模式
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
CustomObject customObject = studentDao.selectCustomObjectById(1001);
System.out.println(customObject.toString());
sqlSession.close();
}
//输出:CustomObject{cid=1001, cname='张三', email='123456@qq.com', age=12}
3.5 自定义别名
mybatis提供的对java对象定义简短,好记的名称.
自定义别名步骤:
- 在mybatis主配置文件,使用typeAliases标签声明别名
- 在mapper文件中,resultType=“别名”
主配置文件:mybatis.xml
<!--别名标签位于<settings>标签之后, 具体顺序可以参照相关的dtd文档-->
<typeAliases>
<!-- 第一种语法格式
type : java类型的全限定名称 (自定义类型)
alias:自定义别名
优点: 别名可以自定义
缺点:每个类型必须单独定义
-->
<!-- <typeAlias type="com.nguyenxb.entity.Student" alias="stu" />-->
<!-- <typeAlias type="com.nguyenxb.pojo.QueryParam" alias="qp"></typeAlias>-->
<!-- 第二种方法
name : 包名 ,mybatis会把这个包中所有类名作为别名(不用区分大小写)
优点:使用方便,一次给多个类定义别名
缺点: 别名不能自定义,必须是类名
-->
<package name="com.nguyenxb.entity"/>
</typeAliases>
mapper文件:
<!--在主配置文件配置别名之后,直接使用别名即可-->
<!--第一种方法-->
<select id="selectStudentById" resultType="stu">
select id,name,email,age from student where id = #{studentId}
</select>
<!--第二种方法-->
<select id="selectStudentById" resultType="student">
select id,name,email,age from student where id = #{studentId}
</select>
3.6 列名和java对象属性名不一样的解决方式
- 使用resultMap:自定义列名和属性名对应关系
- 使用resultType: 使用列的别名和java对象的名称一样
dao接口定义
CustomObject selectCustomObjectById2(@Param("stuid") Integer id);
mapper文件
<!-- 使用列别名,解决列名和属性名不同的问题-->
<select id="selectCustomObjectById2" resultType="com.nguyenxb.pojo.CustomObject">
select id as cid,name cname,email,age from student where id = #{stuid}
</select>
测试
@Test
public void selectCustomObjectById2(){
// 获取 sqlSession操作数据
SqlSession sqlSession = MyBatisUtil.getSqlSession();
// 使用代理模式
StudentDao dao = sqlSession.getMapper(StudentDao.class);
CustomObject customObject = dao.selectCustomObjectById2(1001);
System.out.println(customObject);
sqlSession.close();
}
// 输出:ustomObject{cid=1001, cname='张三', email='123456@qq.com', age=12}
3.7 like 模糊查询
第一种方式
在java程序中, 把like的内容组装好.把这个内容传入到sql语句
dao接口定义
// like第一种方式
List<Student> selectLikeOne(@Param("name") String name);
mapper文件
<select id="selectLikeOne" resultType="com.nguyenxb.entity.Student">
select * from student where name like #{name}
</select>
测试
@Test
public void testSelectLikeOne(){
// 获取 sqlSession操作数据
SqlSession sqlSession = MyBatisUtil.getSqlSession();
// 使用代理模式
StudentDao dao = sqlSession.getMapper(StudentDao.class);
String name = "%阿%";
List<Student> students = dao.selectLikeOne(name);
students.forEach(student -> System.out.println(student));
sqlSession.close();
}
/*输出:
Student{id=1004, name='阿瑟东', email='asd1@qq.com', age=23}
Student{id=1005, name='阿瑟东', email='asd2@qq.com', age=23}
Student{id=1006, name='阿迪', email='adi1@qq.com', age=15}
Student{id=1007, name='阿萨', email='as@qq.com', age=30}
Student{id=1008, name='阿迪', email='adi2@qq.com', age=15}
*/
第二种方式
在sql语句种,组织like的内容.
sql语句like的格式: where name like “%” #{name} “%”
注意: “%” #{name} “%”
, “%” 与 #{name} 之间存在空格.
dao接口
// like第二种方式
List<Student> selectLikeTwo(@Param("name") String name);
mapper文件
<!-- like 第二种方式-->
<!--对应的sql语句为:select * from student where name like "%" ? "%" -->
<select id="selectLikeTwo" resultType="com.nguyenxb.entity.Student">
select * from student where name like "%" #{name} "%"
</select>
测试
@Test
public void testSelectLikeTwo(){
// 获取 sqlSession操作数据
SqlSession sqlSession = MyBatisUtil.getSqlSession();
// 使用代理模式
StudentDao dao = sqlSession.getMapper(StudentDao.class);
String name = "阿";
List<Student> students = dao.selectLikeTwo(name);
students.forEach(student -> System.out.println(student));
sqlSession.close();
}
/* 输出
Student{id=1004, name='阿瑟东', email='asd1@qq.com', age=23}
Student{id=1005, name='阿瑟东', email='asd2@qq.com', age=23}
Student{id=1006, name='阿迪', email='adi1@qq.com', age=15}
Student{id=1007, name='阿萨', email='as@qq.com', age=30}
Student{id=1008, name='阿迪', email='adi2@qq.com', age=15}
*/
第四章 动态SQL
- 什么是动态SQL:
动态SQL, 通过mybatis提供的各种标签对条件作出判断以实现动态拼接SQL语句.这里的条件判断表达式为OGNL表达式.常用的动态SQL标签有<if>,<where><choose>,<foreach>
等.语法格式与JSTL类似.
- 说明
同一个dao的方法,根据不同的条件可以表示不同的sql语句,主要是where部分有变化
使用动态sql的时候,dao方法的形参使用java对象.
- 什么时候使用动态sql?
在多条件查询时使用动态sql
实体符号表
符号 | 说明 | html实体 |
---|---|---|
< | 小于 | < |
> | 大于 | > |
<= | 小于等于 | <= |
>= | 大于等于 | >= |
4.1 if标签
语法
注: 只有if,没有else
<if test="boolean判断结果">
sql 代码
</if>
基本用法
mapper文件中
<select id="selectStudent" resultType="com.nguyenxb.entity.Student">
select * from student
<if test="条件">
sql语句1
</if>
<if test="条件">
sql语句2
</if>
</select>
其中 sql语句 : select * from student
称为主sql 语句, if标签中的 sql语句1和sql语句2称为部分sql 语句. if标签可以有多个
当<if>
标签满足条件时,会将<if>
标签中的部分sql语句拼接至主sql语句的后面
例子
dao接口
// if 使用动态sql 时, 参数使用对象
List<Student> selectIf(Student student);
mapper文件
<!--if
test: 使用对象的属性值作为条件
-->
<select id="selectIf" resultType="com.nguyenxb.entity.Student">
select * from student
where
<if test="name != null and name !=''">
name = #{name}
</if>
<if test="age > 0">
or age < #{age}
</if>
</select>
测试
@Test
public void selectIf(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
Student student = new Student();
// 测试1 : 查询成功:查询 名字是阿迪的或者年龄小于20的
// mybatis生成的sql语句:select * from student where name = ? or age < ?
student.setName("阿迪");
student.setAge(20);
// 测试2 : 查询成功
// mybatis生成的sql语句:select * from student where name = ?
// student.setName("阿迪");
// 测试3 : 查询失败, sql语句语法错误 :
//mybatis生成的sql语句 : select * from student where or age > 20
// student.setName(null);
// student.setAge(20);
List<Student> students = dao.selectIf(student);
students.forEach(stu -> System.out.println(stu));
sqlSession.close();
}
4.2 where 标签
使用if标签时,容易引起sql语句语法错误. 使用where标签可以解决if产生的语法问题.
使用where标签时,里面是一个或者多个if标签,当有一个if标签 判断条件为true时, where标签会转为 WHERE 关键字 附加到主sql 语句的后面.如果if标签没有一个条件为true, 则忽略添加where和里面的if. where标签会删除 和 它 直接连接的 or | and
语法:
<where>
<if test="条件1">sql语句1</if>
<if test="条件2">sql语句2</if>
</where>
dao接口
// where 标签
List<Student> selectWhere(Student student);
mapper文件
<select id="selectWhere" resultType="com.nguyenxb.entity.Student">
select * from student
<where>
<if test="name != null and name !=''">
or name = #{name}
</if>
<if test="age > 0">
or age > #{age}
</if>
</where>
</select>
测试
@Test
public void selectWhere(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
Student student = new Student();
// 测试1 : 查询成功, :查询 名字是阿迪的或者年龄大于20的
// mybatis生成的sql语句 :select * from student WHERE name = ? or age > ?
student.setName("阿迪");
student.setAge(20);
// 测试2: 查询成功
//mybatis生成的sql语句 : select * from student WHERE name = ?
// student.setName("阿迪");
// 测试3 : 查询成功 ,查询年龄大于20的
// mybatis生成的sql语句 :select * from student WHERE age > ?
student.setName(null);
student.setAge(20);
List<Student> students = dao.selectWhere(student);
students.forEach(stu -> System.out.println(stu));
sqlSession.close();
}
4.3 foreach 循环
java拼接sql语句: 查询 id在idList中的学生信息
@Test
public void testForEach(){
List<Integer> idList = new ArrayList<>();
idList.add(1001);
idList.add(1002);
idList.add(1003);
// 拼接sql语句
StringBuilder sb = new StringBuilder();
sb.append("select * from student where id in ");
sb.append("("); // 循环开始时的字符
// 追加idList的数据到sql字符串
for (int i = 0; i < idList.size(); i++) {
// 获取idList中的成员
Integer item = idList.get(i);// 集合中的成员
sb.append(item);
sb.append(",");//集合成员之间的分隔符
}
// 将数据后面的最后一个逗号删除
sb.deleteCharAt(sb.length()-1);
sb.append(")");//循环结束时的字符
System.out.println(sb);
// 输出 : select * from student where id in (1001,1002,1003)
}
语法格式:
<foreach collection="集合类型" open="开始的字符" close="结束的字符" item="集合中的成员" separator="集合成员之间的分隔符">
#{item的值}
</foreach>
标签属性:
collection: 表示 循环的对象是数组还是list集合.
如果dao接口方法的形参是 数组, 则 collection="array" .
如果dao接口方法的形参是 List, 则 collection = "list".
open : 循环开始时的字符. 即 : sb.append("(");
close: 循环结束时的字符. 即 : sb.append(")");
item : 集合成员, 自定义的遍历. 即 : Integer item = idList.get(i);
separator : 集合成员之间的分隔符. 即 : sb.append(",");
#{item的值}: 获取集合成员的值.
<foreach>
标签用于实现对数组与集合的遍历,对其使用,需要注意:
- collection表示要遍历的集合类型,list, array等.
- open,close,separator 为对遍历内容的SQL拼接.
- 一般使用在in语句中.
使用foreach 第一种方式
dao接口定义
List<Student> selectForeach1(List<Integer> idList);
mapper文件
<select id="selectForeach1" resultType="com.nguyenxb.entity.Student">
select * from student
<if test="list != null and list.size > 0">
where id in
<foreach collection="list" open="(" close=")" separator="," item="myId">
#{myId}
</foreach>
</if>
</select>
测试
@Test
public void selectForeach1(){
List<Integer> idList = new ArrayList<>();
idList.add(1001);
idList.add(1002);
idList.add(1003);
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
// mybaitis生成的sql语句: select * from student where id in ( ? , ? , ? )
List<Student> students = dao.selectForeach1(idList);
students.forEach(stu-> System.out.println(stu));
sqlSession.close();
}
使用foreach 第二种方式
dao接口定义
List<Student> selectForeach2(List<Student> studentList);
mapper文件 , 其中 #{stu.id} , 表示 取出student对象的id属性的值,其他同理.
<select id="selectForeach2" resultType="com.nguyenxb.entity.Student">
select * from student
<if test="list != null and list.size > 0">
where id in
<foreach collection="list" open="(" close=")" separator="," item="stu">
#{stu.id}
</foreach>
</if>
</select>
测试
@Test
public void selectForeach2(){
List<Student> idList = new ArrayList<>();
Student student = new Student();
Student student1 = new Student();
student.setId(1004);
student1.setId(1005);
idList.add(student);
idList.add(student1);
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
List<Student> students = dao.selectForeach2(idList);
students.forEach(stu-> System.out.println(stu));
sqlSession.close();
}
4.4 代码片段
sql标签标识一段sql代码,可以是表名, 几个字段,where条件都可以. 可以在其他地方复用sql标签的内容.
使用方式:
在mapper文件中定义 sql代码片段: <sql id="唯一字符串">部分sql语句 </sql>
在其他的位置,使用include标签来 引用 某个代码片段
比如, 将使用foreach第一种方式案例的mapper修改如下,也可以完成查询
<!--代码片段定义 -->
<sql id="SelectStudentField">
id,name,email,age
</sql>
<sql id="selectStudent">
select <include refid="SelectStudentField"/> from student
</sql>
<!--使用代码片段-->
<select id="selectForeach1" resultType="com.nguyenxb.entity.Student">
<include refid="selectStudent"></include>
<if test="list != null and list.size > 0">
where id in
<foreach collection="list" open="(" close=")" separator="," item="myId">
#{myId}
</foreach>
</if>
</select>
第五章 MyBatis配置文件
5.1 配置文件
mybatis配置文件两大类:
-
mybatis主配置文件
说明: mybatis主配置文件, 提供mybatis的全局设置.包含的内容,日志,数据源,以及mapper文件的位置
-
mybatis的mapper文件
说明: mybatis的mapper文件, 写sql语句的. 一般一个表写一个mapper文件
5.2 setting部分
settings是mybatis的全局设置,影响整个mybatis的运行. 这个设置一般使用默认值就可以了.
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
5.2 typeAliase别名
设置别名
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
5.3 配置环境
environments: 环境标签, 在它里面可以配置多个environment
属性: default,必须是某个environment的id属性值.表示mybatis默认连接的数据库.
environment : 表示一个数据库的连接信息.
属性: id自定义的环境表示.唯一值
transactionManager: 事务管理器
属性: type 表示事务管理器的类型
属性值:
(1) jdbc: 使用connction对象,由mybatis自己完成事务的处理.
(2)MANAGED: 管理,表示把数位的处理交给容器实现(由其他软件完成事务的提交,回滚)
dataSource: 数据源, 创建Connection对象,连接数据库.
属性: type 数据源的类型
属性值:
(1) POOLED, mybatis会在内存中创建PooledDataSource类型,管理多个Connection连接对象,使用的是连接池.(常用这个)
(2) UNPOOLED,不使用连接池,mybatis创建一个UnPooledDataSource这个类,每次执行sql语句下创建Connection对象,在执行sql语句,最后关闭Connection.
(3) JNDI: java的命名和目录服务
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<!--配置数据源: 创建Connection对象-->
<dataSource type="POOLED">
<!--driver:驱动的内容-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url: 不能直接使用 & 符号,要将其转换为 html 实体即 & 代表的就是 & 符号 -->
<property name="url" value="jdbc:mysql://localhost:3306/ssm?
useSSL=true&useUnicode=true&characterEncoding=utf-8"/>
<!--用户名-->
<property name="username" value="root"/>
<!--用户密码-->
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
5.4 使用数据库的属性配置文件
需要把数据库的配置信息放到一个单独文件中,独立管理. 这个文件的扩展名是properties.在这个文件中,使用自定义的key=value的格式表示数据
使用步骤:
- 在resources目录中,创建 xxx.properties
- 在文件中,使用key=value的格式定义数据.
- 例如: jdbc.url = jdbc:mysql://localhost:3306/springdb
- 在mybatis主配置文件,使用
properties
标签引用外部的属性配置文件 - 在使用值的位置,使用
${key}
获取key对应的value(即等号右侧的值)
示例:
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?
useSSL=true&useUnicode=true&characterEncoding=utf-8"
jdbc.username=root
jdbc.password=root
mybatis.xml
在mybatis文件中的configuration的标签里面的第一个标签的位置设置外部配置文件
<!-- 使用外部属性配置文件
resource: 指定类路径下的某个属性的文件
-->
<properties resource="jdbc.properties"/>
在mybatis.xml中使用外部属性配置文件中的内容,用法如下:
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!--配置数据源: 创建Connection对象-->
<dataSource type="POOLED">
<!--driver:驱动的内容-->
<property name="driver" value="${jdbc.driver}"/>
<!--连接数据库的url: 不能直接使用 & 符号,要将其转换为 html 实体即 & 代表的就是 & 符号 -->
<property name="url" value="${jdbc.url}"/>
<!--用户名-->
<property name="username" value="${jdbc.username}"/>
<!--用户密码-->
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
5.5 mapper标签
使用mapper标签指定其他mapper文件的位置
mapper标签使用的格式有两个常用的方式: 注意不要重复加载mapper
<mappers>
<!-- 第一种方式, resource="mapper文件的路径
优点:文件清晰.加载的文件是明确的.文件的位置比较灵活.
缺点:文件笔记多,代码量会比较大,管理难度大.
"-->
<mapper resource="com\nguyenxb\dao\StudentDao.xml"/>
<!-- 第二种方式, 使用<package>
name : 包名, mapper文件所在的包名.
特点:把这个包中的所有mapper文件,一次加载.
使用要求:
1.mapper文件和dao接口在同一个目录
2.mapper文件和dao接口名称完全一样
-->
<package name="com.nguyenxb.dao"/>
</mappers>
第六章 PageHelper
PageHelper做数据分页.在你的select语句后面加入分页的sql内容,如果你使用的mysql数据库,他就是在select * from student 加入 limit语句.
使用步骤:
-
加入pagehelper依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.10</version> </dependency>
-
在mybatis的主配置文件,加入plugins声明 , 注意 : 在environments标签之前加入
<!-- 声明插件--> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin> </plugins>
例子:
dao接口定义
// pageHelper 用法
List<Student> selectAllStudents();
mapper文件
<select id="selectAllStudents" resultType="com.nguyenxb.entity.Student">
select * from student order by id
</select>
测试
@Test
public void testSelectAllStudents(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
//调用PageHelper的方法
PageHelper.startPage(1,3);
List<Student> students = dao.selectAllStudents();
students.forEach(stu-> System.out.println(stu));
sqlSession.close();
}
测试输出:
未调用PageHelper的方法,输出:
mybatis生成的sql语句 : select * from student order by id
/*
Student{id=1001, name='张三', email='123456@qq.com', age=12}
Student{id=1003, name='哈哈', email='haha@qq.ocm', age=20}
Student{id=1004, name='阿瑟东', email='asd1@qq.com', age=23}
Student{id=1005, name='阿瑟东', email='asd2@qq.com', age=23}
Student{id=1006, name='阿迪', email='adi1@qq.com', age=15}
Student{id=1007, name='阿萨', email='as@qq.com', age=30}
Student{id=1008, name='阿迪', email='adi2@qq.com', age=15}
*/
调用PageHelper的方法,输出:
mybatis生成的sql语句 :
当页数不是第一页时, 即如测试2,mybatis生成的sql语句:
select * from student order by id LIMIT ?, ?
当页数是第一页时, 即 如 测试1和 3, mybatis生成的sql语句:
select * from student order by id LIMIT ?
/*
测试1 :
调用PageHelper的方法,即PageHelper.startPage(1,3)输出:
Student{id=1001, name='张三', email='123456@qq.com', age=12}
Student{id=1003, name='哈哈', email='haha@qq.ocm', age=20}
Student{id=1004, name='阿瑟东', email='asd1@qq.com', age=23}
测试2 :
调用PageHelper的方法,即PageHelper.startPage(2,3)输出:
Student{id=1005, name='阿瑟东', email='asd2@qq.com', age=23}
Student{id=1006, name='阿迪', email='adi1@qq.com', age=15}
Student{id=1007, name='阿萨', email='as@qq.com', age=30}
测试3:
调用PageHelper的方法,即PageHelper.startPage(1,4)输出:
Student{id=1001, name='张三', email='123456@qq.com', age=12}
Student{id=1003, name='哈哈', email='haha@qq.ocm', age=20}
Student{id=1004, name='阿瑟东', email='asd1@qq.com', age=23}
Student{id=1005, name='阿瑟东', email='asd2@qq.com', age=23}
*/