本文已参与「新人创作礼」活动,一起开启掘金创作之路。
2.4 MyBatis
- Mybatis 是一款优秀的持久层框架,用于简化 JDBC开发,它支持定制化 SQL、存储过程以及高级映射。
- 持久层就是负责将数据保存到数据库的那一层代码。
- JavaEE三层架构:表现层、业务层、持久层。
- 表现层:页面展示的。
- 业务层:使用来做逻辑处理的。
- MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了[google code](baike.baidu.com/item/google code/2346604),并且改名为MyBatis 。2013年11月迁移到Github。
- 英文网
- 中文网
- mybatis-spring
2.4.1 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">
<modelVersion>4.0.0</modelVersion>
<!--当前项目的坐标-->
<groupId>com.itlaity</groupId>
<artifactId>maven-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<!--导入mysql 驱动 jar包-->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!--单元测试的坐标-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!-- mybatis 依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!-- 添加日志支持 -->
<!-- log4j日志系统 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- simple logg facade for java(slf4-api.jar)日志接口 和 log4j具体日志系统之间的适配器 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.20</version>
</dependency>
<!-- 添加logback-classic依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 安装logback-core 依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</project>
放到src/main/resources/ 的日志配置文件logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 级别从高到低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL -->
<!-- 日志输出规则 根据当前ROOT 级别,日志输出时,级别高于root默认的级别时 会输出 -->
<!-- 以下 每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志 -->
<!-- scan 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 -->
<!-- scanPeriod 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds"
debug="false">
<!-- 动态日志级别 -->
<jmxConfigurator />
<!-- 定义日志文件 输出位置 -->
<property name="log_dir" value="../logs" />
<!-- <property name="log_dir" value="/home/data/logs/src" /> -->
<!-- 日志最大的历史 30天 -->
<property name="maxHistory" value="30" />
<!-- ConsoleAppender 控制台输出日志 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
<!-- 设置日志输出格式 -->
[%d{yyyy-MM-dd HH:mm:ss.SSS}][%logger:%line]%-5level -- %msg%n
</pattern>
</encoder>
</appender>
<logger name="com.itlaity" level="DEBUG" additivity="false">
<appender-ref ref="console"/>
</logger>
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender -->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录WARN级别的日志 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR,INFO,WARN,DEBUG</level>
</filter>
<!-- 最常用的滚动策略,它根据时间来制定滚动策略.既负责滚动也负责出发滚动 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志输出位置 可相对、和绝对路径 -->
<fileNamePattern>
${log_dir}/%d{yyyy-MM-dd}/logback.log
</fileNamePattern>
<maxHistory>${maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>
<!-- 设置日志输出格式 -->
[%d{yyyy-MM-dd HH:mm:ss.SSS}][%logger:%line]%-5level -- %msg%n
</pattern>
</encoder>
</appender>
<root>
<!-- 打印TRACE级别日志及以上级别日志 -->
<level value="DEBUG" />
<!-- 控制台输出 -->
<appender-ref ref="console" />
<!-- 文件输出 -->
<appender-ref ref="file" />
</root>
</configuration>
彩色日志bogback.xml(了解)
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="APP_NAME" value="MY_APP_NAME" />
<property name="LOG_DIR" value="logs" />
<property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{15} - %msg%n" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %boldYellow([%thread]) %cyan(%logger{15}) %msg%n"/>
<contextName>${APP_NAME}</contextName>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${LOG_DIR}/logFile.log</file>
<append>true</append>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/dayLogFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 使用root的appender-ref -->
<logger name="com.example.Logger1" level="DEBUG" additivity="true">
</logger>
<!-- 不使用root的appender-ref -->
<logger name="com.example.Logger2" level="DEBUG" additivity="false">
</logger>
<logger name="com.example.Logger3" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
<appender-ref ref="RollingFile" />
</root>
</configuration>
放到src/main/resources/ 的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>
<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:///java?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="wang9264"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--加载sql映射文件 自建-->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
放到src/main/resources/ 的映射的 SQL 语句文件
注意文件名称一定是有标识性的 例如:对用户表进行操作 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">
<!--
namespace:名称空间
resultType:对应你建立的包名
id:唯一标识不能重复
-->
<mapper namespace="test">
<select id="selectAll" resultType="com.itlaity.pojo.User">
select * from mybatis_user;
</select>
</mapper>
开始写代码src\main\java\com\itlaity\pojo\User.java
package com.itlaity.pojo;
/**
* @author : Laity
* @Project: JavaLaity
* @Package com.itlaity
* @date Date : 2021年12月12日 1:37
* @Description: 实体类
*/
// 按住atl+鼠标左键 整列编辑
public class User {
private Integer id;
private String username;
private String password;
private char gender;
private String addr;
// 添加get和set方法
/**
* 在java中,为了保证数据的安全性,
* 我们会把数据定义为private等(私有、封装),
* 如果想要调用就会用到set()方法与get方法或者构造函数方法。
* 这里说的是第一种方法,set()与get()
*/
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", gender=" + gender +
", addr='" + addr + '\'' +
'}';
}
}
测试类src\main\java\com\itlaity\MyBatisDemo.java
package com.itlaity;
import com.itlaity.pojo.User;
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.InputStream;
import java.util.List;
/**
* @author : Laity
* @Project: JavaLaity
* @Package com.itlaity
* @date Date : 2021年12月12日 1:55
* @Description: 测试类
*/
public class MyBatisDemo {
public static void main(String[] args) throws Exception {
// 1. 加载mybatis的核心配置文件,获取 SqlSessionFactory类的对象
String resource = "mybatis-config.xml";
// 返回字节输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 获取 SqlSession对象 执行sql语句
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3. 执行sql
List<User> users = sqlSession.selectList("test.selectAll");
System.out.println(users);
// 释放资源
sqlSession.close();
}
}
如果你认为麻烦
同学我送你一句话: 今朝有酒,今朝醉。明日愁来,明日愁。
2.4.2 Mapper代理开发
- 解决原生方法中的硬编码
- 简化后期执行SQL
设置SQL映射文件的namespace
-
定义一个与sql映射文件同名的mapper接口,并且将Mapper接口和sql映射文件放置在同一目录下(创建目录用 / 来创建)
-
属性为Mapper接口权限定名
-
在mapper接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致
-
List<User> selectAll();
-
-
编码
- 通过SqlSession的getMapper方法获取 Mapper接口的代理对象
- 调用对应方法完成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">
<!--
namespace:名称空间
resultType:对应你建立的包名
id:唯一标识不能重复
-->
<!--设置SQL映射文件的namespace属性为Mapper接口权限定名-->
<mapper namespace="com.itlaity.mapper.UserMapper">
<select id="selectAll" resultType="com.itlaity.pojo.User">
select *
from mybatis_user;
</select>
</mapper>
2.4.3 特殊字符处理
// 在编写sql语句中
参数占位符
1.#{} :执行sql时,会将#{}占位符替换成 ?,将来自动设置参数值
2.${} :拼sql,会存在sql注入问题。
parameterType:
用于设置参数类型,该参数可以省略。
< 是写不了的 怎么解决呢
1.转义字符
2.CDATA区
2.4.4 注解完成增删改查
-
增(insert)
- 注意默认开启事务:需要手动提交事务 sqlSession.commit();
- 或者在获取 SqlSession时 添加参数 true
- 主键返回:在数据添加成功后,需要获取插入数据库的主键的值
- 只需在sql语句中参加两个属性: useGeneratedKeys 和 KeyProperty
- 返回结果 void
-
修改(updata)
- 修改全部字段 上面的JDBC就已经学过
- 修改动态字段 书写动态SQL语句
- where id = #{id}
-
删除(delete)
-
删除一个
- delete from 表 where id = #{id}
-
删除多个
-
void deleteByids(@Param("ids") int[] ids) // @Param("ids")注解改变map集合的默认key的名称 -
delete from 表 where id in <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id} </foreach>
-
-
-
结论 :大多数参数都封装为Map集合,可以使用@Param 注解,替换Map集合中默认的arg键名 param键名。
2.4.5 动态SQL
// 在编写sql语句中(多条件查询)
动态sql(搜索添加一个需要三个参数,但用户只输入1个参数)
1.我们需加条件判断即可
2.<if test="参数 != null and 参数!= '' "> 执行要求参数 </if>
3.问题: 第一个条件不需要逻辑运算符
1. 解决方案 :恒等式 1=1/ <where> 替换 where 关键字
// 单条件查询
<choose> <!--相当于 switch-->
<when> <!--相当于 case-->
</when>
<otherwise><!--default-->
1 = 1
</otherwise>
</choose>
2.4.6 注解开发
- 查询:@Select
- 添加:@Insert
- 修改:@Updata
- 删除:@Delete
- 提示:注解完成简单功能,配置文件完成复杂功能。
// 在maaper接口中写
@select("sql语句")
User selectById();
// UserMapper.xml中就不需要写了