【Mybatis】
1.JDBC不足
JDBC作为Java操作数据库的模板,如果想要对数据库进行操作,必须使用JDBC,但是在使用JDBC进行数据库操作时,重复代码多,动态SQL构建繁琐,将查询结果转化为对象,相当麻烦,开发效率低。
基于JDBC开发效率相对低的情况,市面上各个组织,对JDBC进行封装,产生各种数据库操作层解决方案:
Hibernate 重量级的ORM框架
ibatis 轻量级ORM框架 与2010-06-16 改名 mybatis
Spring JPA
Mybatis plus
Spring JDBCTemplate
以上框架都是对JDBC的封装,处理Java操作数据库数据的问题。
2.为什么要学习框架
2.1.1 提高开发效率
在Java中,框架在一定程度就是对某些功能的封装,对外暴露统一操作API,可以简化开发难度,提高开发效率。
2.1.2 提高代码的可维护性
由于框架在一定程度上说,就是模板,所有基于某个框架进行开发的项目,肯定要遵循模板规则,那么在维护时,只需要了解模板规则即可。
2.1.3 可以提高代码的健壮性
市面上相对比较流行的框架,被大多人使用,出现的问题能够及时暴露,问题也能得到较快的修复。
3.Mybatis概述
3.1 简介
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
Mybtais是一个ORM框架,轻量级的ORM框架。相对于重量级的ORM框架Hibernate而言,mybatis是一个半自动框架,而Hibernate是一个全自动框架。并且,Hibernate提出跨"平台",Hinernate的跨平台是指Hinernate可以在多种数据库下进行操作,同一套代码支持多种数据库,HQL语句,Hibernate方言,对数据库操作进行翻译,根据不同的数据,将API翻译成不同的SQL,对数据进行操作,依赖ORM思想。
Mybatis是一个半自动的框架,早期Hibernate在流行时,开发者发现Hibernate虽然功能强大,但是由于如果想使用全自动功能,将Hibernate和数据库关心进行配置,配置很繁琐,其二,Hibernate对功能进行全方面的封装,将用户的操作,转化为SQL语句,然后进行数据库操作,整个转换过程是Hibernate,开发无法控制,如果要进行SQL语句优化是没法实现的。所以,在数据库压力逐渐增大的情况下,Hibernate框架性能问题就出现了。基于这样的情况,Mybatis框架应运而生,mybatis将SQL语句的定义控制权,完全交给了开发者,并且暴露一套API,对JDBC中:事务,参数,查询结果等进行配置处理。Mybatis也是基于ORM思想.
Mybatis(半自动化):还是需要书写sql
Hibernate(全自动化):不需要写sql就能操作数据库—>HQL
3.2 ORM
ORM : Object relation mapping
对象关系映射
将数据库信息和Java中实体类进行映射
users: 1 张三 20 User: id、uName、age
4.Mybatis入门使用
4.1 创建数据库和表
4.2 创建空项目
4.3 创建Maven的java项目
4.4 引入Mybatis相关jar包依赖
1、导入依赖
2、书写mybatis的主配置文:连接信息 mybatis.xml
3、书写存放sql语句的局部配置文件 xxx.xml(与dao层类的类名保持一致)
4、关联主配置与局部配置
5、启动mybatis,调用核心类测试
<dependencies>
<!--引入mybatis的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!--引入mysql的驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
</dependencies>
4.5 编写实体类User
package com.bjpowernode.domain;
import java.util.Date;
/**
* @项目 code-mybatis
* @描述 用户实体类
* @时间 2021-07-15 22:23
**/
public class User {
private Integer id;
private String name;
private Integer age;
private String sex;
private String address;
private Date birth;
public User(Integer id, String name, Integer age, String sex, String address, Date birth) {
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
this.address = address;
this.birth = birth;
}
public User(String name, Integer age, String sex, String address, Date birth) {
this.name = name;
this.age = age;
this.sex = sex;
this.address = address;
this.birth = birth;
}
public User() {
}
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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
", birth=" + birth +
'}';
}
}
4.6 编写映射文件UserMapper接口
package com.bjpowernode.mapper;
import com.bjpowernode.domain.User;
import java.util.Map;
public interface UserMapper {
/**
* 根据ID查询 返回 Map
* @param id
* @return
*/
Map<String,Object> queryById1(Integer id);
/**
* 根据ID查询 返回 自定义对象
* @param id
* @return
*/
User queryById2(Integer id);
}
4.7 编写映射文件UserMapper.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.bjpowernode.mapper.UserMapper">
<select id="queryById1" resultType="java.util.Map">
select * from user where id = 1
</select>
<select id="queryById2" resultType="com.bjpowernode.domain.User">
select * from user where id = 1
</select>
</mapper>
4.8 编写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.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&useSSL=false&characterEncoding=UTF8&serverTimezone=UTC" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
4.9 编写测试类
public class Test {
public static void main(String[] args) throws Exception {
//1.引入配置文件
String config = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(config);
//2.解析配置文件 构建 SqlSession 构建对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//3.使用构建对象构建 SqlSessionFactory 对象 创建Sql会话对象 程序和数据库进行SQL会话对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
//4.创建一个具体的SqlSession 对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//5.进行数据库具体sql会话
User user = sqlSession.selectOne("com.example.mapper.UserMapper.queryById2",1);
System.out.println(user);
//6.关闭会话
sqlSession.close();
}
}
4.10 执行过程简单分析
1.解析了xml 配置文件 —> configuration
-
将映射文件信息 解析 mappedStatements map结构 每个 mapper文件中的指令 被封装为MappedStatement
-
SqlSession 根据传入 statement 的key 从mappedStatements 中获取到对应 MappedStatement
-
MappedStatement 中已经定义了好了参数类型 返回结果的类型 还有原生sql
-
获取原生sql 参数类型 返回结果的类型 ,根据参数类型 原生sql 可以内部预处理 sql 将sql 发送给数据库
-
接收sql 执行的结果 ,将其封装为对应的返回类型
5.Mybatis进行CRUD
5.1 方式 一:指令ID方式
根据mybatis将映射信息封装为:MappedStatement对象,存在Map中,将namespace和指令ID当做key
根据key获取对应MappedStatement对象,进行数据库操作
5.1.1 创建项目修改pom.xml引入相关依赖
<dependencies>
<!--引入mybatis的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--引入mysql的驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
</dependencies>
5.1.2 复制上个项目里面的所有类和配置
5.1.3 SqlSessionUtils工具类
/**
* 创建SqlSession的工具类
*/
public class SqlSessionUtils {
private static final SqlSessionFactory sqlSessionFactory ;
static {
//配置文件
String config = "mybatis.xml";
//将配置文件转化为流对象
InputStream in = null;
//创建SqlSessionFactory的构建对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = null;
try {
//将配置文件转化为流对象
in = Resources.getResourceAsStream(config);
//创建SqlSessionFactory的构建对象
sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
} catch (IOException e) {
e.printStackTrace();
}
//通过配置信息 构建 SqlSessionFactory工厂
sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
}
/**
* 获取SQL会话对象
* @return
*/
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
}
5.1.4 数据库数据操作类UserDao
package com.bjpowernode.dao;
import com.bjpowernode.domain.User;
import com.bjpowernode.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
/**
* @项目 code-mybatis
* @描述 TODO
* @时间 2021-07-15 22:39
**/
public class UserDao {
public List<User> selectAll() {
SqlSession session = SqlSessionUtils.getSession();
List<User> list = session.selectList("com.bjpowernode.mapper.UserMapper.selectAll");
session.close();
return list;
}
public User queryById(int id) {
SqlSession session = SqlSessionUtils.getSession();
User user = session.selectOne("com.bjpowernode.mapper.UserMapper.selectUser", id);
session.close();
return user;
}
public int addUser(User user) {
SqlSession session = SqlSessionUtils.getSession();
int result = session.insert("com.bjpowernode.mapper.UserMapper.addUser", user);
session.commit();
session.close();
return result;
}
public int updateUserById(User user) {
SqlSession session = SqlSessionUtils.getSession();
int result = session.update("com.bjpowernode.mapper.UserMapper.updateUser", user);
session.commit();
session.close();
return result;
}
public int deleteUserById(int id) {
SqlSession session = SqlSessionUtils.getSession();
int result = session.delete("com.bjpowernode.mapper.UserMapper.deleteUser", id);
session.commit();
session.close();
return result;
}
}
5.1.5 数据库数据操作映射配置UserMapper.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.bjpowernode.mapper.UserMapper">
<!-- 查询所有用户 -->
<select id="selectAll" resultType="com.bjpowernode.domain.User">
select * from user
</select>
<!-- 查询单个用户 -->
<select id="queryById" resultType="com.bjpowernode.domain.User">
select * from user where id = #{id}
</select>
<!-- 添加用户 -->
<insert id="addUser" parameterType="com.bjpowernode.domain.User"
useGeneratedKeys="true">
insert into user(name,age,sex,address,birth) values(#{name},#{age},#{sex},#{address},#{birth})
</insert>
<!-- 更新用户信息 -->
<update id="updateUserById" parameterType="com.bjpowernode.domain.User">
update user set name=#{name},age=#{age},sex=#{sex},address=#{address},birth=#{birth} where id=#{id}
</update>
<!-- 删除用户 -->
<delete id="deleteUserById">
delete from user where id=#{id}
</delete>
</mapper>
5.1.6 测试类
public class Test {
public static void main(String[] args) throws Exception {
UserDao userDao = new UserDao();
List<User> userList = userDao.selectAll();
userList.forEach(System.out::println);
}
}
5.2 方式二:接口代理方式
使用Mapper接口的代理方式,根据方法名和映射文件中指令ID进行绑定,动态调用相关指令信息
5.2.1 创建项目修改pom.xml引入相关依赖
<dependencies>
<!--引入mybatis的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--引入mysql的驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
</dependencies>
5.2.2 复制上个项目里面的所有类和配置文件
删除 dao
5.2.3 创建UserMapper接口
package com.bjpowernode.mapper;
import com.bjpowernode.domain.User;
import com.bjpowernode.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
import java.util.Map;
public interface UserMapper {
/**
* 全查询
* @return
*/
public List<User> selectAll();
/**
* 根据ID查询一个
* @return
*/
public User queryById(int id);
/**
* 添加用户
* @return
*/
public int addUser(User user);
/**
* 修改用户
* @return
*/
public int updateUserById(User user);
/**
* 删除用户
* @return
*/
public int deleteUserById(int id);
}
5.2.4 UserMapper.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.bjpowernode.mapper.UserMapper">
<!-- 查询所有用户 -->
<select id="selectAll" resultType="com.bjpowernode.domain.User">
select * from user
</select>
<!-- 查询单个用户 -->
<select id="queryById" resultType="com.bjpowernode.domain.User">
select * from user where id = #{id}
</select>
<!-- 添加用户 -->
<insert id="addUser" parameterType="com.bjpowernode.domain.User"
useGeneratedKeys="true" keyProperty="aaa">
insert into user(name,age,sex,address,birth) values(#{name},#{age},#{sex},#{address},#{birth})
</insert>
<!-- 更新用户信息 -->
<update id="updateUserById" parameterType="com.bjpowernode.domain.User">
update user set name=#{name},age=#{age},sex=#{sex},address=#{address},birth=#{birth} where id=#{id}
</update>
<!-- 删除用户 -->
<delete id="deleteUserById">
delete from user where id=#{id}
</delete>
</mapper>
5.2.5 测试类
public class Test {
public static void main(String[] args) throws Exception {
SqlSession session = SqlSessionUtils.getSession();
//获取UserMapper接口的具体代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> userList = mapper.selectAll();
userList.forEach(System.out::println);
}
}
6.Mybatis核心配置文件详解
6.1 数据源配置详解
<?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">
<!-- JDBC – 这个配置直接使用JDBC 的提交和回滚功能。它依赖于从数据源获得连接来管理
事务的生命周期。
• MANAGED – 这个配置基本上什么都不做。它从不提交或者回滚一个连接的事务。而是让
容器(例如:Spring 或者J2EE 应用服务器)来管理事务的生命周期
-->
<transactionManager type="JDBC" />
<!--
数据源类型:
UNPOOLED – 这个类型的数据源实现只是在每次需要的时候简单地打开和关闭连接。
POOLED – 这个数据源的实现缓存了JDBC 连接对象,用于避免每次创建新的数据库连接时都初始
化和进行认证,加快程序响应。并发WEB 应用通常通过这种做法来获得快速响应。
JNDI – 这个数据源的配置是为了准备与像Spring 或应用服务器能够在外部或者内部配置数据
源的容器一起使用,然后在JNDI 上下文中引用它
-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&useSSL=false&characterEncoding=UTF8&serverTimezone=UTC" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
6.2 引入db.properties配置文件详解
6.2.1 创建db.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&useSSL=false&characterEncoding=UTF8&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
6.2.2 修改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>
<!--外部配置文件配置-->
<properties resource="db.properties"></properties>
<!--环境配置-->
<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="mapper/UserMapper.xml"/>
</mappers>
</configuration>
6.3 别名优化详解
【注意】:别外配置必须是在enviroments之前
<typeAliases>
<!-- 为指定类型指名 别名 使得在mapper映射文件中可以简化引用
<typeAlias type="com.bjpowernode.domain.User" alias="User"/>
-->
<!-- 为某个包下的所有类指定别名 默认别名是对应的类名 -->
<package name="com.bjpowernode.domain"/>
</typeAliases>
6.4 日志配置详解:有可能会报找不到日志类的异常
日志,就是将程序运行时,一些关键信息进行保存文件中的操作.当问题发生时,是无法观察控制台,将关键的信息保存在文件,便于后期检查。
日志主要分为以下级别:
debug调试模式时进行记录的调试信息
info具体信息
error 发生异常时可以进行记录的方法
debug级别最低
info级别高于DEBUG
error级别最高
6.4.1 引入日志相关jar包
<!-- 日志相关依赖 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
6.4.2 创建log4j.properties的日志配置文件
# 全局日志配置
log4j.rootLogger=DEBUG, stdout,D,E
# MyBatis 日志配置
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
6.4.3 修改mybatis.xml配置文件
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
6.4.4 测试并查看E盘的logs目录
7.Mybatis映射配置文件详解
7.1 结果映射
在mybatis中,默认将查询的结果返回一个实体类对象,查询结果和实体类根据结果列别名和类中属性名一一对应。当查询结果列别名和类属性名不一致。解决方案有两种:
l 将别名修改为一致
l 使用mybatis内置结果映射处理器
7.1.1 在映射文件中新增映射关系
<!--
id 结果映射关系的唯一标识
type 对应的类
-->
<resultMap id="BaseResultMap" type="com.example.domain.User">
<!-- id 一般在逻辑上标识 是主键
column : 查询结果的列别名
property : 类中属性名
-->
<id column="id" property="id"/>
<!--
result 和 id 是没有区别的
只是 id 用于标识是主键
-->
<result column="user_name" property="name" />
<result column="pwd" property="pwd" />
</resultMap>
<!-- 查询一个 -->
<select id="selectOne" resultMap="BaseResultMap">
select id,name as user_name,pwd from user where id = #{id}
</select>
7.1.2 注意
l 映射关系中,column查询结果的列别名,不是原始列名
l 在查询指令中,resultMap属性,指向定义的映射关系的ID
7.2 #和$(重点)
在mybatis中,会将开发者定义的sql进行解析,解析分为了2类sql:
静态sql ,在解析时,直接将参数拼接到sql中,这种就是静态sql conn.createStatement()
动态sql,在解析时,会使用?这个占位符,替代参数
conn.prepareStatement()
这两种解析方式,mybatis是根据${}和#{}进行区分的
${}的sql是静态sql ${uName}
#{}的sql是动态sql #{uName} #{arg0} #{param1} #{0}
不论是静态sql,还是动态sql都能获取传递参数,但是${}是使用的字符拼接,#{}使用PreparedStatement进行参数的预处理。
在一定程度上说,${}能实现的功能,#{}都能实现,并且由于#{}PreparedStatement进行SQL的预处理,一定程度上可以防止SQL注入攻击。所以在开发中,能使用#{}尽量使用#{}。
PreparedStatement预处理的本质是将参数进行转换为字符串,当做参数字符串处理。所以,如果参数信息是一个特殊的关键字,例如: 数据库名,表名,函数名,内置关键字,使用预处理,则关键字转为了字符串,无效了,此时必须使用字符串拼接。
7.2.1 $示例
<!-- 查询一个 -->
<select id="selectOne" resultMap="BaseResultMap">
select id,name as user_name,pwd from user where id = ${id}
</select>
7.2.2 #示例
<select id="selectUser" resultType="com.example.domain.User">
select * from user where id = #{id}
</select>
7.3 SQL片段
在开发中,需要书写大量的SQL语句,并且这些SQL数据很大部分内容是重复内容,基于这样的情况,mybatis提供模板,可以在模板中定义需要使用sql语句的部分内容,然后在需要使用到这个部分内容的地方直接引入。
使用sql标签,定义SQL片段,使用include引入,sql片段
<sql id="BASE_COLUMN">
id,name,pwd
</sql>
<!-- 查询单个用户 -->
<select id="selectUser" resultType="com.example.domain.User">
select
<include refid="BASE_COLUMN">
</include>
from user where id = #{id}
</select>
注意:
l 在sql片段中 id,是这个sql片段的唯一标识
l 在引入时,include标签中,refid关联sql片段的id
7.4 模糊查询(重点)
在mybatis中,模糊查询主要有3种方式:
l like concat(‘%’,#{关键字},‘%’) 推荐
l like ‘%${关键字}%’
l mybatis推荐的bind标签
7.4.1 方案一[推荐]
<!-- 查询一个 -->
<select id="selectByName" resultMap="BaseResultMap">
select
<include refid="BASE_COLUMN" />
from user where name like concat('%',#{name},'%')
</select>
7.4.2 方案二[拼接]
使用$存在sql注入攻击安全问题
<select id="selectByName" resultMap="BaseResultMap">
select
<include refid="BASE_COLUMN" />
from user where name like '%${name}%'
</select>
7.4.3 方案三[bind]
<select id="selectByName" resultMap="BaseResultMap">
<bind name="keyWord" value="new String('%'+name+'%')"/>
select
<include refid="BASE_COLUMN" />
from user where name like #{keyWord}
</select>
7.5 多个参数问题(重点)
当mybatis传递参数存在多个时,mybatis支持三种方案:
argx 形式:arg 表示参数,x表示参数索引
paramX形式: param表示参数,x表示第几个参数
使用注解为参数取别名 @Param
方案三
/**
* 根据名字和ID进行搜索
* @param name
* @param id
* @return
*/
public List<User> selectByNameAndId(@Param("name") String name, @Param("id") Integer id);
<select id="selectByNameAndId" resultMap="BaseResultMap">
select
<include refid="BASE_COLUMN" />
from user where name like concat('%',#{name},'%') and id = #{id}
</select>
7.6 分页查询(重点)
在mybatis中,分页查询解决方案最本质上回归原生SQL进行处理。
l 查询总页数,使用limit函数进行查询相应的数据
l mybatis中,提供了一个RowBounds这个类,用于进行分页查询数据
l 使用分页插件:PageHelper(推荐)
7.6.1 方案一[rowBounds]
使用RowBounds这种分页查询方式,在实际开发中是远远不够使用的,这种方式只是返回的相应的数据,总数据条数,页数,分页起始页都需要开发者自己处理。
/**
* mybatis 推荐的分页方式
* @param rowBounds
* @return
*/
public List<User> selectPage(RowBounds rowBounds);
<!-- 查询所有 -->
<select id="selectPage" resultType="User">
select <include refid="BASE_COLUMN" /> from user
</select>
public class Test {
public static void main(String[] args) throws Exception {
SqlSession session = SqlSessionUtil.getSession();
//获取UserMapper接口的具体代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
RowBounds rowBounds = new RowBounds(0,5);
List<User> users = mapper.selectPage(rowBounds);
users.forEach((u) -> System.out.println(u));
session.commit();
session.close();
}
}
7.6.2 方案二[分页插件]
市面主流分页插件: PageHelper
导入PageHelper的相关jar包
<!-- 分页插件jar -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
或者
jsqlparser-3.2.jar
pagehelper-5.2.0.jar
在mybatis核心配置文件中开启插件
<!-- 插件 -->
<plugins>
<!--
在mybatis 为了让开发者能够更好扩展mybatis功能 ,mybatis提供了插件接口
这样开发者可以通过实现接口 重写方法,对mybatis 功能进行扩展(通过拦截进行扩展)
-->
<!--
分页插件 :PageHelper
-->
<!-- 配置分页插件 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor" />
</plugins>
在代码显示声明后面紧邻(一次)查询语句需要使用分页
在PageHelper,每次开启分页,只对最近的一次查询有效。如果要多次分页查询,需要多次开启。
PageHelper是利用,拦截器原理,拦截最近的一次查询,然后在其基础拼接分页信息。PageHelper只对最近一次查询有效,这种模式,对本身的程序没有侵入,所以,本身程序该怎么写还是怎么写。
public static void main(String[] args) throws Exception {
SqlSession session = SqlSessionUtil.getSession();
//获取UserMapper接口的具体代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
//开启分页 2 页码 5 每页数据条数
Page<User> userPage = PageHelper.startPage(2, 5);
//进行查询 PageHelper 会对查询进行拦截处理 将将要进行的查询语句进行分页处理
mapper.selectAll();
System.out.println("总页数:"+userPage.getPages());
System.out.println("页码:"+userPage.getPageNum());
System.out.println("每页条数:"+userPage.getPageSize());
System.out.println("总条数:"+userPage.getTotal());
List<User> users = userPage.getResult();
users.forEach((u) -> System.out.println(u));
session.commit();
session.close();
}
7.7 新增自增长(重点)
在mybatis中,支持数据库的自增长功能,因为在某些特殊的业务场景中,当前数据的ID,可能是另外某些数据的业务ID。
例如:
订单:总订单和子订单
每个子订单会有总订单ID
先插入总订单,并且要获取总订单ID
插入子订单
Mybatis在insert指令中,提供了2个属性:useGeneratedKeys、keyProperty、keyColumn
useGeneratedKeys:表示使用数据库自增长
keyProperty:自增长的列对应的类中的属性
keyColumn:自增长的列
Mybatis会自动将增长的值,封装到传入参数的属性中。
<insert id="insert" parameterType="com.example.domain.User" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into user (name,pwd) value (#{name},#{pwd})
</insert>
public static void main(String[] args) throws Exception {
SqlSession session = SqlSessionUtil.getSession();
//获取UserMapper接口的具体代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User();
user.setName("Lucy");
user.setPwd("123456");
System.out.println("新增后user对象:" + user.getId());
session.commit();
session.close();
}
注意:
自增长值,会被自动封装在对象参数中
7.8 动态SQL(重点)
动态SQL是指根据不同的参数,产生不同的SQL语句。这样的SQL就是动态SQL。
Mybatis提供了一下标签,用于动态SQL的生成:
if
foreach
choose
where: 可以去除前置多余的and、or等挂关键字;不能去除后置
set
trim
7.8.1 if标签
在if标签中,test属性是必须有,test属性值是一个表达式,如果表达式值为true,则if标签包裹的内容会拼接在当前sql上。
and 并且
or 或者
== 等于
!= 不等于
示例:
<!-- 条件查询 -->
<select id="selectList" resultType="com.example.domain.User">
select <include refid="BASE_COLUMN" /> from user where 1=1
<if test="name != null and name !=''">
and name like concat('%',#{name},'%')
</if>
</select>
7.8.2 foreach
循环标签,循环标签多用于批量操作。
例如:批量新增,批量删除等等
collection 待循环的容器
item 指代 每次循环容器中的元素
open 开始循环是拼接字符串
close 循环结束拼接字符串
separator 每次循环之间拼接的字符串
index 循环索引
(#{user.name},#{user.pwd}) delete from user where id in #{id} choose多条件分支标签:choose.
在choose标签,自上而下执行when中表达式,如果表达式为true,则将相应的字符串拼接在sql后面,且终止判断。如果所有的表达式都为false,则将otherwise中字符串,拼接在sql后面
select from user where age > 19 age > 18 18 > ageList selectByAge(@Param(“age”) Integer age);
public static void main(String[] args) {
SqlSession session = SqlSessionUtil.getSession();
//获取UserMapper接口的具体代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
Integer age = 17;
List<User> users = mapper.selectByAge(age);
session.commit();
session.close();
}
7.8.3 where
在mybatis中,存在sql条件,当有多个sql条件时,需要处理and关键字问题,因为where后面的第一个条件不需要and,解决方案:
l 在where后面 拼接 1=1 类似的条件,这样其他条件都不是第一个条件,都需要拼接and
l mybatis 提供了where标签,取代where关键字,默认去掉第一个条件 and
注意:
建议,只在查询语句中使用where标签,因为当where标签中的条件都不成立时,会没有where关键字。
select from user and name like concat('%',#{name},'%') and sex = #{sex} delete from user name = #{name}7.8.4 set标签
set标签是取代sql语句中的set关键字。set表示后面数据的更新。各个字段,需要使用逗号分隔。
set标签可以去掉最后一个逗号。
update user name = #{name}, pwd = #{pwd}, where id = #{id}8. 缓存
mybatis为了提高查询效率、性能。提供了缓存,缓存分为2类:
l 一级缓存
l 二级缓存
一级缓存:是指SqlSession级别的缓存,在同一个SqlSession,同样的SQL语句只会执行一次,不是第一次执行的SQL会从缓存中获取数据。
二级缓存: 是指SqlSessionFactory级别的缓存,在同一个SqlSessionFactory中,同样的SQL语句,只会执行一次,不是第一次执行的SQL会从缓存中获取数据。
注意:
不论一级缓存还是二级缓存,否是JVM中缓存。当服务器有多台时,缓存容易发生无效,数据发生了更新。缓存没有更新。在实际使用,如果数据可能发生比较频繁的更新不建议使用mybatis。
8.1 一级缓存
public static void main(String[] args) {
SqlSession session = SqlSessionUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
//第一次查询
System.out.println(“==================================”);
List users = mapper.selectAll();
//第二次查询
session.commit();
session.close();
session = SqlSessionUtil.getSession();
mapper = session.getMapper(UserMapper.class);
System.out.println("************************************");
users = mapper.selectAll();
users.forEach(user -> System.out.println(user.getName()));
session.commit();
session.close();
}
8.2 二级缓存
二级缓存需要在映射文件中,开启缓存:
并且被缓存的数据需要支持,序列化
9.Mybatis注解
mybatis配置虽然相对简单,但是还是麻烦。所以,mybatis为了简化配置,也提供了注解。
常用注解:
l @Select
l @Insert
l @Delete
l @Update
public interface UserMapper {
@Insert(“insert into user (name,pwd) value (#{name},#{pwd})”)
Integer insert(User user);
@Delete("delete from user where id = #{id}")
Integer delete(Integer id);
@Select("select id,name,pwd from user ")
List<User> selectAll();
/* 动态sql 写法 */
@Select("<script>select id,name,pwd from user <where> <if test='id != null'>id > 18</if> </where></script>")
List<User> selectList(@Param("age") Integer age);
}
10.逆向工程
1、 快速帮我们生成实体类
2、生成单表的crud
11.联表查询(难点)
11.1 多表查询解决方案
1.写实体类,通过定义实体类,将多表的所有字段都涵盖,解决多表查询问题,但是,这种方案只能解决多对一,但是无法解决一对多问题。
例如:写实体类可以解决,一个学生类中班级信息问题,但是无法解决班级类中学生信息问题。
学生信息是一个数组。
2.多次查询。将需要结果拆分成多个简单的单表查询,连续查询多次。使用Java代码处理查询结果。
例如:学生班级信息。
查询某个学生学生信息及其班级信息。
根据ID查询学生信息
从学生信息中获取班级ID
根据班级ID查询班级信息
查询所有学生的信息及其班级信息
查询所有学生(100)(注意学生数据量)
查询所有班级(Map 使用ID 作为key 对象就是value)
循环学生,获取班级ID,从班级数据中,获取班级信息
注意:
mybatis解决联表查询的方法就是基于以上方案
CREATE TABLE student
(
id
int(11) NOT NULL AUTO_INCREMENT,
sname
varchar(255) DEFAULT NULL,
cid
int(11) DEFAULT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE classes
(
id
int(11) NOT NULL,
cname
varchar(255) DEFAULT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
11.2 多对一
11.2.1 将查询结果进行封装
<?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.example.mapper.StudentMapper">
<resultMap id="STUDENT_CLASSES_MAP" type="Student">
<result column="id" property="id" />
<result column="sname" property="sname" />
<result column="cid" property="cid" />
<!--
mybatis 提供的关联标签 多对一
property :当前类中关联属性
javaType : 属性所属的类
-->
<association property="classes" javaType="com.example.domain.Classes">
<result column="cid" property="cid" />
<result column="cname" property="cname" />
</association>
</resultMap>
<!-- 使用实体类的思想 -->
<select id="selectOne" resultType="map">
select s.id,s.sname,s.cid,c.cname from student s left join classes c on s.cid = c.cid
where s.id = #{id}
</select>
<select id="selectOne2" resultMap="STUDENT_CLASSES_MAP">
select s.id,s.sname,s.cid,c.cname from student s left join classes c on s.cid = c.cid
where s.id = #{id}
</select>
</mapper>
package com.example.mapper;
import com.example.domain.Student;
import org.apache.ibatis.annotations.Param;
import java.util.Map;
public interface StudentMapper {
Map<String,Object> selectOne(@Param("id") Integer id);
Student selectOne2(int i);
}
package com.example.test;
import com.example.domain.Classes;
import com.example.domain.Student;
import com.example.mapper.StudentMapper;
import com.example.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.Map;
/**
* @Description:
* @date 2020-09-24 09:32
*/
public class Test {
public static void main(String[] args) {
SqlSession session = SqlSessionUtil.getSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
//自己将map数据处理成封装的对象
Map<String, Object> map = mapper.selectOne(1);
Student student = new Student();
student.setId((Integer) map.get("id"));
student.setSname(map.get("sname").toString());
student.setCid((Integer) map.get("cid"));
Classes classes = new Classes();
classes.setCid((Integer) map.get("cid"));
classes.setCname(map.get("cname").toString());
student.setClasses(classes);
System.out.println("转化的student:"+student);
System.out.println(map);
System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
//直接获取mybatis的封装对象
Student student1 = mapper.selectOne2(1);
System.out.println(student1);
session.commit();
session.close();
}
}
package com.example.domain;
import lombok.Data;
/**
* @Description: 学生类
* @date 2020-09-24 10:55
*/
@Data
public class Student {
private Integer id; //学生ID
private String sname;//学生名称
private Integer cid;//班级ID
private Classes classes;//班级信息
}
package com.example.domain;
import lombok.Data;
import java.util.List;
/**
* @Description: 班级类
* @date 2020-09-24 10:56
*/
@Data
public class Classes {
private Integer cid;//班级ID
private String cname;//班级名称
private List<Student> students; //学生信息
}
11.2.2 进行多次查询
<resultMap id="STUDENT_CLASSES_MAP2" type="Student">
<result column="id" property="id" />
<result column="sname" property="sname" />
<result column="cid" property="cid" />
<association property="classes" javaType="com.example.domain.Classes" select="com.example.mapper.ClassesMapper.selectById" column="cid" />
</resultMap>
<select id="selectAll" resultMap="STUDENT_CLASSES_MAP2">
select
s.id,s.sname,s.cid
from
student s
</select>
<?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.example.mapper.ClassesMapper">
<select id="selectById" resultType="Classes">
select cid ,cname from classes where cid = #{cid}
</select>
</mapper>
List<Student> selectAll();
package com.example.test;
import com.example.domain.Student;
import com.example.mapper.StudentMapper;
import com.example.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
/**
* @Description:
* @date 2020-09-24 09:32
*/
public class Test {
public static void main(String[] args) {
SqlSession session = SqlSessionUtil.getSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> students = mapper.selectAll();
System.out.println(students);
session.commit();
session.close();
}
}
11.3 一对多
11.3.1 将查询结果封装
<?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.example.mapper.ClassesMapper">
<resultMap id="CLASSES_STUDENT_MAP" type="Classes">
<result column="cid" property="cid" />
<result column="cname" property="cname" />
<!--
collection 表示 一对多
ofType 表示集合中 存储数据类型化
result 映射关系
-->
<collection property="students" ofType="com.example.domain.Student">
<result column="id" property="id" />
<result column="sname" property="sname" />
<result column="cid" property="cid" />
</collection>
</resultMap>
<select id="selectById" resultType="Classes">
select cid ,cname from classes where cid = #{cid}
</select>
<select id="selectAll" resultMap="CLASSES_STUDENT_MAP">
select c.cid ,c.cname,s.id,s.sname from classes c left join student s on c.cid = s.cid
</select>
</mapper>
package com.example.mapper;
import com.example.domain.Classes;
import java.util.List;
/**
* @Description: TODO
* @date 2020-09-24 10:58
*/
public interface ClassesMapper {
List<Classes> selectAll();
}
package com.example.test;
import com.example.domain.Classes;
import com.example.mapper.ClassesMapper;
import com.example.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
/**
* @Description:
* @date 2020-09-24 09:32
*/
public class Test {
public static void main(String[] args) {
SqlSession session = SqlSessionUtil.getSession();
ClassesMapper mapper = session.getMapper(ClassesMapper.class);
List<Classes> classes = mapper.selectAll();
System.out.println(classes);
session.commit();
session.close();
}
}
11.3.2 多次查询
<resultMap id="CLASSES_STUDENT_MAP2" type="Classes">
<result column="cid" property="cid" />
<result column="cname" property="cname" />
<!--
collection 表示 一对多
ofType 表示集合中 存储数据类型化
result 映射关系
property 关联的属性值
select 获取property 执行 select指定的查询 将查询结果设置给property 属性
-->
<collection property="students" ofType="com.example.domain.Student" select="com.example.mapper.StudentMapper.selectAllByCId" column="cid" />
</resultMap>
<select id="selectAll2" resultMap="CLASSES_STUDENT_MAP2">
select c.cid ,c.cname from classes c
</select>
<select id="selectAllByCId" resultType="Student">
select
id,sname,cid
from
student s where cid = #{cid}
</select>
package com.example.mapper;
import com.example.domain.Classes;
import java.util.List;
/**
* @Description: TODO
* @date 2020-09-24 10:58
*/
public interface ClassesMapper {
List<Classes> selectAll();
List<Classes> selectAll2();
}
package com.example.test;
import com.example.domain.Classes;
import com.example.mapper.ClassesMapper;
import com.example.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
/**
* @Description:
* @date 2020-09-24 09:32
*/
public class Test {
public static void main(String[] args) {
SqlSession session = SqlSessionUtil.getSession();
ClassesMapper mapper = session.getMapper(ClassesMapper.class);
List<Classes> classes = mapper.selectAll2();
System.out.println(classes);
session.commit();
session.close();
}
}