准备:
maven:https://mvnrepository.com/
mybatis: MyBatis中文网
1.mybatis的定义
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
持久化:
- 持久化是将程序数据在持久状态和瞬时状态间转换的机制。(理解一下这个持久状态转化为瞬时状态,持久化是mybatis最重要的特性)。即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。
- 持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。JDBC就是一种持久化机制。文件IO也是一种持久化机制。
为什么要持久化:
- 因为内存断点后数据会丢失,但是有些业务不允许这种情况的存在。
- 比起硬盘,内存过于昂贵,如果有够量的内存,则不需要持久化服务,但是正是因为内存太贵,储存有限,因此需要持久化来缓存。
持久层:
- 完成持久化工作代码块,即Dao层
- 大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。
mybatis的作用:
- Mybatis就是帮助程序员将数据存取到数据库里面。
- 传统的jdbc操作 , 有很多重复代码块,简化开发 。比如 : 数据取出时的封装 , 数据库的建立连接等等… , 通过框架可以减少重复代码,提高开发效率 。
- MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射。
- 所有的事情,不用Mybatis依旧可以做到,只是用了它,会更加方便更加简单,开发更快速。
mybatis的优点:
- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件就可以了,易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
- 提供xml标签,支持编写动态sql。
- 现在主流使用方法。
2.搭建mybatis
地址:https://mvnrepository.com/artifact/org.mybatis
依赖:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.8</version>
</dependency>
1.创建mysql数据库:
2.步骤
- 创建新项目,删除src
搭建环境(maven、数据库等)--->导入mybatis依赖-->编写java代码--->测试
- 导入mybatis依赖
Pom.xml:文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Mybatis-study</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mybatis-01</artifactId>
<!-- maven资源过滤-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
mybatis的核心配置文件:连接数据的配置文件,包括账户、密码、时区、编码等配置。
<?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核心配置文件-->
<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/mybatis?useSSl=trur&sueUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 注册每一个mapper文件-->
<mappers>
<mapper resource="com/jiang/dao/UserMapper.xml"></mapper>
</mappers>
</configuration>
- 创建新的模块
3.编写Mybatis的工具类:用于获取SqlSession对象:
package com.jiang.utils;
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;
/**
* @author Lenovo
* @date 2024/3/16
* @time 15:53
* @project Mybatis-study
**/
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
//1. 获取mybatis-config的配置文件
String resource = "mybatis-config.xml";
try {
// 2. 获得SqlSessionFactory工厂:
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了sqlSessionFactory,顾名思义,我们就可以从中获得sqlSession的实例了
//3.sqlSession 完全包含了面向数据库执行SQL命令所需的所有方法
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
4.编写java代码:
(一) 编写实体类:
package com.jiang.pojo;
/**
* @author Lenovo
* @date 2024/3/16
* @time 15:59
* @project Mybatis-study
**/
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
(二) 编写Dao接口:
package com.jiang.dao;
import com.jiang.pojo.User;
import java.util.List;
/**
* @author Lenovo
* @date 2024/3/16
* @time 16:00
* @project Mybatis-study
**/
public interface UserDao {
List<User> getUserList();
}
(三) 编写mapper文件:
<?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">
//一定要指定namespace ,其次是注册mapper文件到mybatis-config文件中
<mapper namespace="com.jiang.dao.UserDao">
<!-- 查询语句:id:是方法: resultType返回为User-->
<select id="getUserList" resultType="com.jiang.pojo.User">
select * from mybatis.user;
</select>
</mapper>
- 编写测试类:
package com.jiang.dao;
import com.jiang.pojo.User;
import com.jiang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* @author Lenovo
* @date 2024/3/16
* @time 16:10
* @project Mybatis-study
**/
public class TetsDao {
SqlSession sqlSession;
@Test
public void test(){
try {
sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭sqlSession
sqlSession.close();
}
}
}
(四) 总结:易错点:
mapper文件中:namespace 中的名字要和接口的路径地址和名字一致。
mybatis-config文件中:
Pom.xml 文件中:maven的资源过滤要加上
<!-- maven资源过滤-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
3.CRUD
(一) 查
List<User> getUserList();
User getUserById(int id);
<!-- 查询语句:resultType返回为User-->
<select id="getUserList" resultType="com.jiang.pojo.User">
select * from mybatis.user;
</select>
<!-- 根据id查询用户-->
<select id="getUserById" resultType="com.jiang.pojo.User" parameterType="int">
select * from mybatis.user where id=#{id};
</select>
id:表示sql语句操作方法的名字;
resultType: 表示返回的类型
parameter: 表示查询参数的类型
SqlSession sqlSession;
@Test
public void test(){
try {
sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}catch (Exception e){
e.printStackTrace();
}finally {
sqlSession.close();
}
}
@Test
public void getUserById(){
sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
User resultUser = mapper.getUserById(2);
System.out.println(resultUser);
sqlSession.close();
}
(二) 增
//增
int addUser(User user);
<insert id="addUser" parameterType="com.jiang.pojo.User">
insert into mybatis.user (id,name,pwd) value (#{id},#{name},#{pwd})
</insert>
@Test
public void addUser(){
sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int resultNumber = mapper.addUser(new User(4, "哈哈", "1254adasd"));
if (resultNumber > 0){
System.out.println("插入成功");
}
//增删改都需要提交事务:
sqlSession.commit();
sqlSession.close();
}
(三) 删
//删
int deleteUser(int id); //根据id 删除对象
删除操作:
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id=#{id}
</delete>
@Test
public void deleteUser(){
sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int i = mapper.deleteUser(4);
if (i >0){
System.out.println("删除操作成功");
}
//增删改都需要提交事务:
sqlSession.commit();
sqlSession.close();
}
(四) 改
//改
int updateUser(User user); //传入一个User对象
<update id="updateUser" parameterType="com.jiang.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id};
</update>
@Test
public void updateUser(){
sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int resultNumber= mapper.updateUser(new User(4,"呵呵","454dsdsf"));
if (resultNumber > 0){
System.out.println("更新成功");
}
//增删改都需要提交事务:
sqlSession.commit();
sqlSession.close();
}
4.Map传入参数
Map:
如果实体类或者数据库中表,字段,参数过多可以使用map集合。
int addUser02(Map<String,Object> map);
//参数类型是map的类型
<insert id="addUser02" parameterType="map">
insert into mybatis.user (id,name,pwd) value (#{userId},#{userName},#{userPwd})
</insert>
@Test
public void addUser02(){
sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("userId","5");
map.put("userName","hehe");
map.put("userPwd","asfsd132123");
int i = mapper.addUser02(map);
if (i>0){
System.out.println("添加一个用户成功");
}
//增删改都需要提交事务:
sqlSession.commit();
sqlSession.close();
}
模糊查询:
- java代码传入通配符:%xxx%
- 在sql中拼接通配符 % xxx %
5.配置解析
mybatis核心配置文件:
(一) 环境配置:environments
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
可以配置多个环境:
<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/mybatis?useSSl=trur&sueUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
mybatis默认使用的事务管理器是:JDBC, 默认使用的数据库连接池是POOLED
(二) 属性:Properties
这些属性可以在外部进行配置,并可以进行动态替换。
进行数据库连接的属性配置:
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSl=true;sueUnicode=true;characterEncoding=UTF-8;serverTimezone=Asia/Shanghai
username=root
password=root
在mybatis-config文件中替换掉数据库的配置:使用${xxx} 来进行替换。
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
注意:
因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。
(三) 设置:settings
设置日志
(四) 类型别名:typeAliases
- 给每一个实体类起别名 (在mybatis-config.xml中加入)
<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>
- 扫描包 (扫描一个包下面所有的类,java 会自动寻找Bean)
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
- 使用注解的方式
在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值
@Alias("author")
public class Author {
...
}
其他配置了解。
6.映射器(mappers)
作用:告诉 MyBatis 到哪里去找映射文件。
1.根据路径:(推荐使用)
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
2.根据class文件去绑定:
注意:但是接口 Dao 和它的Mapper文件要在同一个包下,而且要同名。
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
3.通过包来去绑定:
注意:但是接口和它的Mapper文件要在同一个包下,而且要同名。
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
7.作用域和生命周期:
不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 (一旦创建就一直存在)。
可以理解为:数据库连接池,一直存在,直到程序结束。
SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。
每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。
- 连接到连接池的一个请求。
- SqlSession 是线程不安全的,因此不可以被共享,所以最佳作用域是请求或者方法作用域。
- 用完之后需要关闭,否则资源被占用。
8.ResultMap(结果集映射)
一、解决实体类属性名和数据库字段名不一致的问题
User类中的password, 而数据库中的字段名是:pwd。
解决:
- 起别名
- 结果集映射:ResultMap
resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。
使用resultMap将实体类的属性名和数据库中的字段不一致解决掉:
<resultMap id="userResultMap" type="User">
<result property="userName" column="name"/>
<result property="password" column="pwd"/>
</resultMap>
<select id="getUserList" resultMap="userResultMap">
select * from mybatis.user;
</select>
9.日志工厂
如果一个数据库操作,出现了异常,就需要排错,这时就可以使用日志工厂。
mybatis可以使用的日志:
SLF4J
LOG4J (需要手动配置一些东西才可以使用)
LOG4J2
JDK_LOGGING
COMMONS_LOGGING
STDOUT_LOGGING (mybatis默认使用的,不需要任何的配置)
设置日志:
使用STDOUT_LOGGING:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
结果:
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 1433666880.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@55740540]
==> Preparing: select * from mybatis.user;
==> Parameters:
<== Columns: id, name, pwd
<== Row: 1, 狂神, 123456
<== Row: 2, 张三, abcdef
<== Row: 3, 李四, 987654
<== Row: 5, hehe, asfsd132123
<== Total: 4
User{id=1, userName='狂神', password='123456'}
User{id=2, userName='张三', password='abcdef'}
User{id=3, userName='李四', password='987654'}
User{id=5, userName='hehe', password='asfsd132123'}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@55740540]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@55740540]
Returned connection 1433666880 to pool.
使用LOG4j日志:
- mybatis-config.xml中配置好LOG4j
<settings>
<setting name="logImpl" value="LOG4j"/>
</settings>
- Pom.xml中导入LOG4j的包的依赖
https://mvnrepository.com/artifact/org.apache.logging.log4j
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- resource目录下:配置一个log4j.properties文件
# priority :debug<info<warn<error
#you cannot specify every priority with different file for log4j
log4j.rootLogger=debug,stdout,info,debug,warn,error
#console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= [%d{yyyy-MM-dd HH:mm:ss a}]:%p %l%m%n
#info log
log4j.logger.info=info
log4j.appender.info=org.apache.log4j.DailyRollingFileAppender
log4j.appender.info.DatePattern='_'yyyy-MM-dd'.log'
#存储在当前目录下的文件
log4j.appender.info.File=./src/log/info.log
log4j.appender.info.Append=true
log4j.appender.info.Threshold=INFO
log4j.appender.info.layout=org.apache.log4j.PatternLayout
log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#debug log
log4j.logger.debug=debug
log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender
log4j.appender.debug.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.debug.File=./src/log/debug.log
log4j.appender.debug.Append=true
log4j.appender.debug.Threshold=DEBUG
log4j.appender.debug.layout=org.apache.log4j.PatternLayout
log4j.appender.debug.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#warn log
log4j.logger.warn=warn
log4j.appender.warn=org.apache.log4j.DailyRollingFileAppender
log4j.appender.warn.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.warn.File=./src/log/warn.log
log4j.appender.warn.Append=true
log4j.appender.warn.Threshold=WARN
log4j.appender.warn.layout=org.apache.log4j.PatternLayout
log4j.appender.warn.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#error
log4j.logger.error=error
log4j.appender.error = org.apache.log4j.DailyRollingFileAppender
log4j.appender.error.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.error.File = ./src/log/error.log
log4j.appender.error.Append = true
log4j.appender.error.Threshold = ERROR
log4j.appender.error.layout = org.apache.log4j.PatternLayout
log4j.appender.error.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
4.写代码:
10.分页
实现limit分页: sql语句的用法
SELECT * FROM USER LIMIT startIndex, pageSize;
<resultMap id="userResultMap" type="User">
<result property="userName" column="name"/>
<result property="password" column="pwd"/>
</resultMap>
<select id="getUserByLimit" parameterType="map" resultMap="userResultMap">
select * from mybatis.user limit #{startIndex},#{pagaSize};
</select>
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
//使用map
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("startIndex",0);
map.put("pagaSize",2);
List<User> userList= mapper.getUserByLimit(map);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
11.使用注解开发:用来替代Mapper.xml
不推荐使用:还是建议使用mapper.xml的方式。
底层是反射机制和动态代理,不用使用Mapper文件;但是只能写简单的sql语句
@param("xxx") 以param中的参数名为查询字段;引用类型不需要加,只有一个基本类型的话也可以不加,但是建议加上。
package com.keafmd.dao;
import com.keafmd.domain.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
/**
* Keafmd
*
* @ClassName: IUserDao
* @Description:
* @author: 牛哄哄的柯南
* @date: 2021-02-16 20:30
*/
/**
* 在mybatis中针对CRUD一共有四个注解
* @Select @Insert @Update @Delete
*/
public interface IUserDao {
/**
* 查询所有用户
* @return
*/
@Select("select * from user")
List<User> findAll();
/**
* 保存用户
* @param user
*/
@Insert("insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday})")
void saveUser(User user);
/**
* 更新用户
* @param user
*/
@Update("update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}")
void updateUser(User user);
/**
* 删除用户
* @param userId
*/
@Delete("delete from user where id=#{id}")
void deleteUser(Integer userId);
/**
* 根据id查询用户
* @param userId
* @return
*/
@Select("select * from user where id=#{id}")
User findById(Integer userId);
/**
* 根据用户名称模糊查询
* @param username
* @return
*/
//@Select("select * from user where username like #{username}") //占位符
@Select("select * from user where username like '%${value}%'") //字符串拼接
List<User> findByName(String username);
/**
* 查询总数量
* @return
*/
@Select("select count(*) from user")
int findTotal();
}
自动提交事务:
12.Lombok的使用:
- 第一步安装lombok的插件
- 第二步导入lombok的依赖
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
常用的注解:
@Data
@Data最常用的注解之一。注解在类上,提供该类所有属性的getter/setter方法,还提供了equals、canEqual、hashCode、toString方法。
@Log4j
作用于类上,为该类提供一个属性名为log的log4j日志对象。
@AllArgsConstructor
作用于类上,为该类提供一个包含全部参的构造方法,注意此时默认构造方法不会提供。
@NoArgsConstructor
作用于类上,提供一个无参的构造方法。可以和@AllArgsConstructor同时使用,此时会生成两个构造方法:无参构造方法和全参构造方法。
不建议使用
13.复杂操作:
结果映射(resultMap)
- constructor - 用于在实例化类时,注入结果到构造方法中
-
- idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
- arg - 将被注入到构造方法的一个普通结果
- id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
- result – 注入到字段或 JavaBean 属性的普通结果
- association – 一个复杂类型的关联;许多结果将包装成这种类型
-
- 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
- collection – 一个复杂类型的集合
-
- 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
- discriminator – 使用结果值来决定使用哪个 resultMap
-
- case – 基于某些值的结果映射
-
-
- 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
-
id和result的属性:
属性 | 描述 |
property | 映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。 |
column | 数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 |
javaType | 一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
jdbcType | JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。 |
typeHandler | 我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。 |
ofType: 指代泛型的类型;
关联:association ;关联查询的时候
<resultMap id="blogResult" type="Blog">
<association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
</resultMap>
<select id="selectBlog" resultMap="blogResult">
SELECT * FROM BLOG WHERE ID = #{id}
</select>
<select id="selectAuthor" resultType="Author">
SELECT * FROM AUTHOR WHERE ID = #{id}
</select>
(一) 多对一的关系:
例子:多个学生对一个老师
mybatis-config.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核心配置文件-->
<configuration>
<!-- 配置数据库连接-->
<properties resource="db.properties"/>
<!-- 设置日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!-- 起别名:-->
<typeAliases>
<typeAlias type="com.jiang.pojo.Teacher" alias="Teacher"/>
<typeAlias type="com.jiang.pojo.Student" alias="Student"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</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/mybatis?useSSl=trur&sueUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 注册每一个mapper文件-->
<mappers>
<mapper class="com.jiang.dao.studentMapper"/>
<mapper class="com.jiang.dao.teacherMapper"/>
</mappers>
</configuration>
package com.jiang.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Lenovo
* @date 2024/3/20
* @time 15:22
* @project Mybatis-study
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
private Teacher teacher;
}
package com.jiang.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Lenovo
* @date 2024/3/20
* @time 15:22
* @project Mybatis-study
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
}
package com.jiang.dao;
import com.jiang.pojo.Student;
import java.util.List;
/**
* @author Lenovo
* @date 2024/3/20
* @time 15:13
* @project Mybatis-study
**/
public interface studentMapper {
//获取所有学生及对应老师的信息
List<Student> getStudents();
}
查询嵌套处理: 按照查询进行嵌套处理就像SQL中的子查询
<?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.jiang.dao.studentMapper">
<!--
需求:获取所有学生及对应老师的信息
思路:
1. 获取所有学生的信息
2. 根据获取的学生信息的老师ID->获取该老师的信息
3. 思考问题,这样学生的结果集中应该包含老师,该如何处理呢,数据库中我们一般使用关联查询?
1. 做一个结果集映射:StudentTeacher
2. StudentTeacher结果集的类型为 Student
3. 学生中老师的属性为teacher,对应数据库中为tid。
多个 [1,...)学生关联一个老师=> 一对一,一对多
4. 查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查询
-->
<select id="getStudents" resultMap="TeacherStudents">
select * from student
</select>
<resultMap id="TeacherStudents" type="Student">
<!--association关联属性 property属性名 javaType:返回的属性类型
column在多的一方的表中的列名
select:表示执行的子查询操作名:
-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id=#{id}
</select>
</mapper>
测试:
package com.jiang.dao;
import com.jiang.pojo.Student;
import com.jiang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* @author Lenovo
* @date 2024/3/20
* @time 15:49
* @project Mybatis-study
**/
public class MyTest {
@Test
public void getStudents(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
studentMapper mapper = sqlSession.getMapper(studentMapper.class);
List<Student> students = mapper.getStudents();
for (Student student : students) {
System.out.println(student);
}
sqlSession.close();
}
}
按照结果嵌套处理:按照结果进行嵌套处理就像SQL中的联表查询
一次查询出所有的结果,将第二张表中的数据进行映射处理。
<!-- 按结果映射-->
<select id="getStudents2" resultMap="TeacherStudents2">
select s.id sid,s.name sname,t.name tname from student s,
teacher t where s.tid = t.id ;
</select>
<resultMap id="TeacherStudents2" type="Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<!--关联对象property 关联对象在Student实体类中的属性-->
<association property="teacher" javaType="Teacher">
<!-- properties: 是在实体中的名字,column是在数据库中的名字-->
<result property="name" column="tname"/>
</association>
</resultMap>
@Test
public void getStudents2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
studentMapper mapper = sqlSession.getMapper(studentMapper.class);
List<Student> students2 = mapper.getStudents2();
for (Student student : students2) {
System.out.println(student);
}
sqlSession.close();
}
(二) 一对多的关系:
例子:一个老师对应多个学生:关联使用collection
package com.jiang.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Lenovo
* @date 2024/3/20
* @time 15:22
* @project Mybatis-study
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
private int tid;
}
package com.jiang.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author Lenovo
* @date 2024/3/20
* @time 15:22
* @project Mybatis-study
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
private List<Student> students;
}
按查询映射:
<!-- 1.按查询映射:-->
<select id="getTeacher" resultMap="TeacherStudents">
select * from teacher where id=#{id}
</select>
<resultMap id="TeacherStudents" type="Teacher">
<!-- 集合
column是一对多的外键 , 写的是一的主键的列名,就是teacher的id -->
<collection property="students" column="id" javaType="ArrayList"
ofType="Student"
select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from student where tid=#{id}
</select>
ofType :是泛型的映射。
按结果进行映射:
<!-- 2.按结果进行映射-->
<select id="getTeacher2" resultMap="TeacherStudents2">
select s.id sid, s.name sname,t.name tname, t.id tid from student s, teacher t
where s.tid = t.id and t.id =#{id};
</select>
<resultMap id="TeacherStudents2" type="Teacher">
<!-- 老师的信息-->
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!-- collection :返回学生的信息-->
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
注意:
association是用于一对一和多对一,而collection是用于一对多的关系
JavaType和ofType都是用来指定对象类型的
- JavaType是用来指定pojo中属性的类型
- ofType指定的是映射到list集合属性中pojo的类型。
14.动态SQL:
准备:
package com.jiang.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author Lenovo
* @date 2024/3/20
* @time 18:48
* @project Mybatis-study
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}
package com.jiang.dao;
import com.jiang.pojo.Blog;
import java.util.List;
/**
* @author Lenovo
* @date 2024/3/20
* @time 18:49
* @project Mybatis-study
**/
public interface BlogMapper {
/**
* 增加一个blog
* @param blog
* @return
*/
int addBlog(Blog blog);
List<Blog> getBlogList();
}
package com.jiang.utils;
import java.util.UUID;
/**
* @author Lenovo
* @date 2024/3/20
* @time 18:47
* @project Mybatis-study
**/
public class IDutils {
public static String getId(){
return UUID.randomUUID().toString().replace("-","");
}
}
<mapper namespace="com.jiang.dao.BlogMapper">
<insert id="addBlog" parameterType="Blog">
insert into mybatis.blog (id,title,author,create_time,views)
values (#{id},#{title},#{author},#{createTime},#{views})
</insert>
<select id="getBlogList" resultType="Blog">
select * from blog
</select>
</mapper>
MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
if:
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
<select id="queryBlogByIf" resultType="Blog" parameterType="map">
select * from blog where
<if test="title !=null">
title=#{title}
</if>
<if test="author !=null">
and author=#{author}
</if>
</select>
<select id="queryBlogByIf2" resultType="Blog" parameterType="map">
select * from blog
<where>
<if test="title !=null">
title=#{title}
</if>
<if test="author !=null">
and author=#{author}
</if>
</where>
</select>
同义替换:where会自动去除and 或者or
select * from blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
@Test
public void queryBlogByIf(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("title","Mybatis如此简单");
map.put("author","狂神说");
List<Blog> blogs = mapper.queryBlogByIf(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
trim:
- where: 条件判断的语句
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
- SET: 用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列. (更新语句update时,去使用)
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
更新操作:
int updateBlog(Map map);
<!-- 更新语句-->
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title !=null">
title=#{title},
</if>
<if test="author !=null">
author=#{author}
</if>
</set>
where id=#{id}
</update>
@Test
public void updateBlog(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("title","python如此简单");
map.put("author","小明");
map.put("id","f7d6ccc28b3a4882adeb86726d354698");
mapper.updateBlog(map);
sqlSession.close();
}
choose:
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
foreach:
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候).
它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符。separator 是分割符。
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
(item1, item2, item3, ....)
collection:是一个list集合类型。
sql标签:抽取公共的部分,在使用的部分使用include包含。
<sql>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
</sql>
例子:
List<Blog> getBlogListByForEach(Map map);
<select id="getBlogListByForEach" parameterType="map" resultType="Blog">
select * from blog
<where>
<!--
collection:指定输入对象中的集合属性的名字
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from blog where 1=1 and (id=1 or id=2 or id=3)
-->
<foreach collection="ids" item="id" open="and (" close=")"
separator="or">
id=#{id}
</foreach>
</where>
</select>
select * from blog where 1=1 and (id=1 or id=2 or id=3) :执行的sql语句,拼接sql语句。
@Test
public void testForEach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, List> map = new HashMap<String, List>();
ArrayList<Integer> idList = new ArrayList<Integer>();
idList.add(1);
idList.add(2);
idList.add(3);
map.put("ids",idList);
List<Blog> blogList = mapper.getBlogListByForEach(map);
for (Blog blog : blogList) {
System.out.println(blog);
}
sqlSession.close();
}
14.缓存
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。
缓存 (只有查询操作才会使用缓存):一次查询的结果,暂时将数据存储到内存中,不需要再次连接数据库,减少系统性能的开销问题。
数据库中重要的概念:读写分离,主从复制原理。
mybatis中的缓存:
- 一级缓存 也叫本地缓存,如果两次查询的结果都是一样的,那么就直接在本地缓存读取,不在再次连接数据库进行查询。 (自动开启) sqlSession。 增删改操作可能会改变数据,所以会刷新缓存。
注意:一级缓存默认是开启的,只在一次sqlSession中才有效,也就是拿到这个数据区间才会有效果。
- 二级缓存 (全局缓存,手动开启)
二级缓存生效: 只有一级缓存结束后,将缓存内容存储到二级缓存中,二级缓存会去开启。开启了二级缓存,缓存只在一个mapper中生效。
(1)mybatis-config.xml中开启全局缓存:
<setting name="cacheEnabled" value="true"/>
(2) 要启用全局的二级缓存,只对一个Mapper文件有效果, 可以自定义缓存的规则:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
mybatis可用的清除策略有:
- LRU – 最近最少使用:移除最长时间不被使用的对象。
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
- WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU。
mybatis 缓存原理:
用户首先会查询二级缓存,如果二级缓存没有接着会查询一级缓存;最后如果缓存中都没有数据就会查询数据库
mybatis 自定义缓存:(了解)
除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。
Ehcache : Java开源的分布式缓存,是一种通用的缓存。
<cache type="com.domain.something.MyCustomCache"/>
自定义:
type 属性指定的类必须实现 org.apache.ibatis.cache.Cache 接口,
且提供一个接受 String 参数作为 id 的构造器。
public interface Cache {
String getId();
int getSize();
void putObject(Object key, Object value);
Object getObject(Object key);
boolean hasKey(Object key);
Object removeObject(Object key);
void clear();
}