Mybits 入门

1简介

1.1什么是Mybits

  • MyBatis 是一款优秀的持久层框架;
  • 它支持自定义 SQL、存储过程以及高级映射。
  •  MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

1.2 什么是持久层

数据持久化

持久化就是将程序的数据在持久状态和瞬时状态转化过程。

内存:断电即失

数据库:io文件持久化

生活:冷藏

为什么需要持久化?

有一些对象不能让他丢掉,内存太贵了。

持久层

Dao层 service 层 controller层

完成持久化工作的代码块

层界限十分明显

1.3为什么需要Mybatis

方便

传统的JDBC代码太复杂,简化,框架,自动化。

帮助程序员将数据存入数据库中。

更容易上手。技术没有高低之分。

优点:

优点:

  • 简单易学
  • 灵活
  • sql和代码的分离,提高了可维护性。
  • 提供映射标签,支持对象与数据库的orm字段关系映射。
  • 提供对象关系映射标签,支持对象关系组建维护。
  • 提供xml标签,支持编写动态sql。

最重要的一点:使用的人多。

2 第一个Mybatis程序

思路:搭建环境 –> 导入MyBatis –> 编写代码 –> 测试

新建项目:

1 新建一个普通meaven项目

2 删除src目录

3 导入meaven依赖

导入相关meaven依赖

<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.21</version></dependency><!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.2</version></dependency>

2.2 创建一个模块

编写核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//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?userSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments>
</configuration>

编写mybatis工具类

package com.kuang.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;public class MybatisUtils {private static SqlSessionFactory sqlSessionFactory;static {//第一步:获取sqlSessionFactory对象String resource = "org/mybatis/example/mybatis-config.xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(resource);} catch (IOException e) {e.printStackTrace();}sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);}public static SqlSession getSqlSession(){return sqlSessionFactory.openSession();}}

2.3 编写代码

实体类

package com.kuang.pojo;public class User {private int id;private String name;private String pwd;public User(int id, String name, String pwd) {this.id = id;this.name = name;this.pwd = pwd;}public User() {}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;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + ''' +", pwd='" + pwd + ''' +'}';}
}

Dao接口

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace=绑定一个指定的Dao/Mapper接口-->
<mapper namespace="com.kuang.dao.UserDao"><select id="getUserList" resultType="com.kuang.pojo.User">select * from USER</select>
</mapper>

接口实现类

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace=绑定一个指定的Dao/Mapper接口-->
<mapper namespace="com.kuang.dao.UserDao"><select id="getUserList" resultType="com.kuang.pojo.User">select * from USER</select>
</mapper>

测试代码 

package com.kuang.dao;import com.kuang.pojo.User;
import com.kuang.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;import java.util.List;public class TestMapper {public static void main(String[] args) {SqlSession sqlSession= MybatisUtil.getSqlSession();UserMapper userMapper=sqlSession.getMapper(UserMapper.class);List<User> userList=userMapper.getUserList();for(User user:userList){System.out.println(user);}sqlSession.close();}
}

  配置文件UserMapper.xml最好放在resources文件夹下

 3CURD

1. namespace

namespace中的包名要和Dao/Mapper接口的包名一致

2. select

选择,查询语句;

  • id:就是对应的namespace中的方法名;

  • resultType : Sql语句执行的返回值;

  • parameterType : 参数类型;

3增删改需要提交事务

sqlSession.commit();

4万能Map

假设,我们的实体类,或者数据库中的表,字段或者参数过多,我们应该考虑使用Map

//用万能Map插入用户
public void addUser2(Map<String,Object> map);

别名是mybatis中的名字。 

xml文件

<!--对象中的属性可以直接取出来 传递map的key-->
<insert id="addUser2" parameterType="map">insert into user (id,name,password) values (#{userid},#{username},#{userpassword})
</insert>

Map传递参数,直接在sql中取出key即可! 【parameter=“map”】

对象传递参数,直接在sql中取出对象的属性即可! 【parameter=“Object”】

只有一个基本类型参数的情况下,可以直接在sql中取到

多个参数用Map , 或者注解!

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace=绑定一个指定的Dao/Mapper接口-->
<mapper namespace="com.kuang.mapper.UserMapper"><select id="getUserList" resultType="com.kuang.pojo.User">select * from USER</select><insert id="addUser" parameterType="com.kuang.pojo.User">insert into user(id,name,pwd) values(#{id},#{name},#{pwd})</insert><insert id="addUser02" parameterType="map">insert into user(id,name,pwd) values(#{Id},#{Name},#{Pwd})</insert><delete id="deleteUserById" parameterType="int">delete from user where id=#{id}</delete><update id="updateUser" parameterType="com.kuang.pojo.User">update user set name=#{name},pwd=#{pwd} where id=#{id}</update><select id="getUserById" parameterType="int" resultType="com.kuang.pojo.User">select * from user where id=#{id}</select>
</mapper>

Usermapper的接口

package com.kuang.mapper;import com.kuang.pojo.User;import java.util.List;
import java.util.Map;public interface UserMapper {public List<User> getUserList();public int addUser(User user);public int addUser02(Map<String,Object>map);public int deleteUserById(int id);public int updateUser(User user);public User getUserById(int id);
}

测试代码

package com.kuang.dao;import com.kuang.mapper.UserMapper;
import com.kuang.pojo.User;
import com.kuang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;import java.util.HashMap;
import java.util.List;
import java.util.Map;public class UserDaoTest {@Testpublic void test(){SqlSession sqlSession= MybatisUtils.getSqlSession();UserMapper userMapper=sqlSession.getMapper(UserMapper.class);List<User> userList=userMapper.getUserList();for(User user:userList){System.out.println(user);}sqlSession.commit();sqlSession.close();}@Testpublic void testadd(){SqlSession sqlSession=MybatisUtils.getSqlSession();UserMapper mapper=sqlSession.getMapper(UserMapper.class);int flag=mapper.addUser(new User(4,"王五","123456"));if(flag>0){System.out.println("插入成功");}else {System.out.println("插入失败");}sqlSession.commit();sqlSession.close();}@Testpublic void testadd02(){SqlSession sqlSession=MybatisUtils.getSqlSession();UserMapper mapper=sqlSession.getMapper(UserMapper.class);Map<String,Object> map=new HashMap<String,Object>();map.put("Id",5);map.put("Name","hahaha");map.put("Pwd","1111111");int flag=mapper.addUser02(map);if(flag>0){System.out.println("插入成功");}else {System.out.println("插入失败");}sqlSession.commit();sqlSession.close();}@Testpublic void testDetele(){SqlSession sqlSession=MybatisUtils.getSqlSession();UserMapper mapper=sqlSession.getMapper(UserMapper.class);int flag=mapper.deleteUserById(5);if(flag>0){System.out.println("删除成功");}else {System.out.println("删除失败");}sqlSession.commit();sqlSession.close();}@Testpublic void testUpdate(){SqlSession sqlSession=MybatisUtils.getSqlSession();UserMapper mapper=sqlSession.getMapper(UserMapper.class);int flag=mapper.updateUser(new User(1,"HAHAHA","111111"));if(flag>0){System.out.println("更新成功");}else {System.out.println("更新失败");}sqlSession.commit();sqlSession.close();}@Testpublic void testGetUser(){SqlSession sqlSession=MybatisUtils.getSqlSession();UserMapper mapper=sqlSession.getMapper(UserMapper.class);User user=mapper.getUserById(1);System.out.println(user);sqlSession.close();}}

4 配置文件解析

1核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//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="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment></environments><mappers><mapper resource="org/mybatis/example/BlogMapper.xml"/></mappers>
</configuration>
configuration(配置)properties(属性)settings(设置)typeAliases(类型别名)typeHandlers(类型处理器)objectFactory(对象工厂)plugins(插件)environments(环境配置)environment(环境变量)transactionManager(事务管理器)dataSource(数据源)databaseIdProvider(数据库厂商标识)mappers(映射器)

2. 环境配置 environments

MyBatis 可以配置成适应多种环境

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境

学会使用配置多套运行环境!

MyBatis默认的事务管理器就是JDBC ,连接池:POOLED

事务管理器(transactionManager)

在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
  • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。

数据源(dataSource)

不止一个类型

POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。

3. 属性 properties

我们可以通过properties属性来实现引用配置文件

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。【db.poperties】

如果两个文件都有相同属性,优先使用外部配置文件

注意properties和typeAliases标签的顺序

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><properties resource="db.properties"/><typeAliases><typeAlias type="com.kuang.pojo.User" alias="User"/></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></environments><mappers><mapper resource="UserMapper.xml"/></mappers>
</configuration>

4. 类型别名 typeAliases

  • 类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置.

  • 意在降低冗余的全限定类名书写。

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><typeAliases><typeAlias type="com.kuang.pojo.User" alias="User"/></typeAliases><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?userSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><mappers><mapper resource="UserMapper.xml"/></mappers>
</configuration>

<typeAliases><typeAlias type="com.kuang.pojo.User" alias="User"/></typeAliases>

也可以制定一个包名,MyBatis会在包名下面搜索需要的Java Bean,比如: 

扫描实体类的包,它的默认别名就是为这个类的 类名,首字母小写

<typeAliases><typeAlias type="com.kuang.pojo.User"/>
</typeAliases>

在实体类比较少的时候,使用第一种方式,如果实体类十分多,建议使用第二种。

 第一种可以自定义别名,第二种不能自定义别名,但是可以通过注解取一个别名。

5. 设置 Settings

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

一个配置完整的 settings 元素的示例如下: 

<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>

6. 其他配置

  • typeHandlers(类型处理器)

  • objectFactory(对象工厂)

  • plugins 插件

    • mybatis-generator-core

    • mybatis-plus

    • 通用mapper

7. 映射器 mappers

MapperRegistry:注册绑定我们的Mapper文件;

方式一:【推荐使用】

<!--每一个Mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers><mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>

方式二:使用class文件绑定注册  

<!--每一个Mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers><mapper class="com.kuang.dao.UserMapper"/>
</mappers>

注意点:

  • 接口和他的Mapper配置文件必须同名

  • 接口和他的Mapper配置文件必须在同一个包下

方式三:使用包扫描进行注入

<mappers><package name="com.kuang.dao"/>
</mappers>

 8. 作用域和生命周期 

声明周期和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder:

  • 一旦创建了SqlSessionFactory,就不再需要它了

  • 局部变量

SqlSessionFactory:

  • 说白了就可以想象为:数据库连接池

  • SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建一个实例。

  • 因此SqlSessionFactory的最佳作用域是应用作用域(ApplocationContext)。

  • 最简单的就是使用单例模式或静态单例模式。

SqlSession:

  • 连接到连接池的一个请求

  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。

  • 用完之后需要赶紧关闭,否则资源被占用!

    5、解决属性名和字段名不一致的问题

    1. 问题

    数据库中的字段

    在这里插入图片描述

    新建一个项目,拷贝之前的,测试实体类字段不一致的情况

    在这里插入图片描述

    测试出现问题
    在这里插入图片描述

// select * from user where id = #{id}
// 类型处理器
// select id,name,pwd from user where id = #{id}

起别名

2. resultMap

结果集映射

id name pwd

id name password

<!--结果集映射-->
<resultMap id="UserMap" type="User"><!--column数据库中的字段,property实体类中的属性--><result column="id" property="id"></result><result column="name" property="name"></result><result column="pwd" property="password"></result>
</resultMap><select id="getUserList" resultMap="UserMap">select * from USER
</select>
  • resultMap 元素是 MyBatis 中最重要最强大的元素。

  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

  • ResultMap 的优秀之处——你完全可以不用显式地配置它们。

  • 如果这个世界总是这么简单就好了。

6、日志

6.1 日志工厂

如果一个数据库操作,出现了异常,我们需要排错,日志就是最好的助手!

曾经:sout、debug

现在:日志工厂

  • SLF4J

  • LOG4J 【掌握】

  • LOG4J2

  • JDK_LOGGING

  • COMMONS_LOGGING

  • STDOUT_LOGGING 【掌握】

  • NO_LOGGIN

在Mybaits中具体使用哪一个日志实现,在设置中设定。

<settings><setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

实质是一个JDBC Connection 

6.2 Log4j

什么是Log4j?

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件;

  • 我们也可以控制每一条日志的输出格式;

  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程;

  • 最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

  1. 导包

  2. 配置资源文件
    #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
    log4j.rootLogger=DEBUG,console,file
    ​
    #控制台输出的相关设置
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target = System.out
    log4j.appender.console.Threshold=DEBUG
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
    #文件输出的相关设置
    log4j.appender.file = org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=./log/jianghe.log
    log4j.appender.file.MaxFileSize=10mb
    log4j.appender.file.Threshold=DEBUG
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
    #日志输出级别
    log4j.logger.org.mybatis=DEBUG
    log4j.logger.java.sql=DEBUG
    log4j.logger.java.sql.Statement=DEBUG
    log4j.logger.java.sql.ResultSet=DEBUG
    log4j.logger.java.sq1.PreparedStatement=DEBUG
    

  3. 配置settings为log4j实现

  4. 测试运行 

Log4j简单使用

  1. 在要使用Log4j的类中,导入包 import org.apache.log4j.Logger;

  2. 日志对象,参数为当前类的class对象

    Logger logger = Logger.getLogger(UserDaoTest.class);
    
    • 1

  3. 日志级别

    logger.info("info: 测试log4j");
    logger.debug("debug: 测试log4j");
    logger.error("error:测试log4j");
    

    将信息输出到日志文件中 

    1. 思考:为什么分页?

    2. 减少数据的处理量   

    3. 7.1 使用Limit分页

    4. SELECT * from user limit startIndex,pageSize 
      
    5. 使用MyBatis实现分页,核心SQL

    6. 接口

      //分页
      List<User> getUserByLimit(Map<String,Integer> map);
      
    7. Mapper.xml

      <!--分页查询-->
      <select id="getUserByLimit" parameterType="map" resultMap="UserMap">select * from user limit #{startIndex},#{pageSize}
      </select>
      
    8. 测试

          @Testpublic void getUserByLimit(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);HashMap<String, Integer> map = new HashMap<String, Integer>();map.put("startIndex",1);map.put("pageSize",2);List<User> list = mapper.getUserByLimit(map);for (User user : list) {System.out.println(user);}}
  4. 7.2RowBounds分页

       7.3分页插件

8、使用注解开发

8.1 面向接口开发

三个面向区别

  • 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性和方法;

  • 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现;

  • 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构;

8.2 使用注解开发

  1. 注解在接口上实现

    @Select("select * from user")
    List<User> getUsers();
    
    • 需要在核心配置文件中绑定接口

  2. <mappers><mapper class="com.kuang.dao.UserMapper"/>
    </mappers>
    
    • 测试

本质:反射机制实现

底层:动态代理

Mybatis执行流程

8.3 注解CURD

//方法存在多个参数,所有的参数前面必须加上@Param("id")注解
@Delete("delete from user where id = ${uid}")
int deleteUser(@Param("uid") int id);

关于@Param( )注解

  • 基本类型的参数或者String类型,需要加上

  •  引用类型不需要加

  • 如果只有一个基本类型的话,可以忽略,但是建议大家都加上

  • 我们在SQL中引用的就是我们这里的@Param()中设定的属性名

#{} 和 ${}


${param}传递的参数会被当成sql语句中的一部分,比如传递表名,字段名例子:(传入值为id)order by ${param} 则解析成的sql为:order by id#{parm}传入的数据都当成一个字符串,会对自动传入的数据加一个双引号例子:(传入值为id)select * from table where name = #{param}则解析成的sql为:select * from table where name = "id"为了安全,能用#的地方就用#方式传参,这样可以有效的防止sql注入攻击

package com.kuang.mapper;import com.kuang.pojo.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;
import java.util.Map;public interface UserMapper {@Select("select * from user")public List<User> getUserList();@Insert("insert into user(id,name,pwd) values(#{id},#{name},#{pwd})")public int addUser(User user);@Insert("insert into user(id,name,pwd) values(#{Id},#{Name},#{Pwd})")public int addUser02(Map<String,Object>map);@Delete("delete from user where id=#{id}")public int deleteUserById(int id);@Update("update user set name=#{name},pwd=#{pwd} where id=#{id}")public int updateUser(User user);@Select("select * from user where id=#{id}")public User getUserById(int id);
}

9、Lombok

Lombok项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符(@Data)就可以替换数百行代码从而产生干净,简洁且易于维护的Java类。

使用步骤:

  1. 在IDEA中安装Lombok插件

  2. 在项目中导入lombok的jar包

    @Getter and @Setter
    @FieldNameConstants
    @ToString
    @EqualsAndHashCode
    @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
    @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
    @Data
    @Builder
    @SuperBuilder
    @Singular
    @Delegate
    @Value
    @Accessors
    @Wither
    @With
    @SneakyThrows
    @val
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {private int id;private String name;private String password;
    }
    

    Lombok本身是一个优秀的Java代码库,它采用了一种取巧的语法糖,简化了Java的编码,为Java代码的精简提供了一种方式,但在使用此代码库时,需要了解到Lombok并非一个标准的Java库。使用Lombok,会增加团队的技术债务,降低代码的可读性,增大代码的耦合度和调式难度。虽然在一定程度上Lombok减少了样板代码的书写,但也带来了一些未知的风险。

    10、多对一处理

    多个学生一个老师;

package com.kuang.mapper;import com.kuang.pojo.Student;public interface StudentMapper {public Student getStudent(int id);
}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace=绑定一个指定的Dao/Mapper接口-->
<mapper namespace="com.kuang.mapper.StudentMapper"><select id="getStudent" parameterType="int" resultMap="StudentAndTeacher">select * from student where id=#{id}</select><resultMap id="StudentAndTeacher" type="Student"><result property="id" column="id"/><result property="name" column="name"/><association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/></resultMap><select id="getTeacher" parameterType="int" resultType="Teacher">select * from teacher where id=#{tid}</select>
</mapper>
package com.kuang;import com.kuang.mapper.StudentMapper;
import com.kuang.mapper.TeacherMapper;
import com.kuang.pojo.Student;
import com.kuang.pojo.Teacher;
import com.kuang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;import java.util.List;public class MyTest {public static void main(String[] args) {SqlSession sqlSession= MybatisUtils.getSqlSession();StudentMapper mapper=sqlSession.getMapper(StudentMapper.class);Student student=mapper.getStudent(1);System.out.println(student);}
}

Association关联的结果查询

  <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace=绑定一个指定的Dao/Mapper接口-->
<mapper namespace="com.kuang.mapper.StudentMapper"><select id="getStudent" parameterType="int" resultMap="StudentAndTeacher">select * from student where id=#{id}</select><resultMap id="StudentAndTeacher" type="Student"><result property="id" column="id"/><result property="name" column="name"/><association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/></resultMap><select id="getTeacher" parameterType="int" resultType="Teacher">select * from teacher where id=#{tid}</select><select id="getStudent02" parameterType="int" resultMap="StudentTeacher2">select s.id sid,s.name sname,t.id tid,t.name tname from student s,teacher t where s.tid=t.id and s.id=#{id};</select><resultMap id="StudentTeacher2" type="Student"><result property="id" column="sid"/><result property="name" column="sname"/><association property="teacher" javaType="Teacher"><result property="id" column="tid"/><result property="name" column="tname"/></association></resultMap>
</mapper>

回顾Mysql多对一查询方式:

  • 子查询 (按照查询嵌套)

  • 联表查询 (按照结果嵌套)

11、一对多处理

一个老师多个学生;

对于老师而言,就是一对多的关系;

<!--按结果嵌套查询-->
<select id="getTeacher" resultMap="StudentTeacher">SELECT s.id sid, s.name sname,t.name tname,t.id tid FROM student s, teacher tWHERE s.tid = t.id AND tid = #{tid}
</select>
<resultMap id="StudentTeacher" type="Teacher"><result property="id" column="tid"/><result property="name" column="tname"/><!--复杂的属性,我们需要单独处理 对象:association 集合:collectionjavaType=""指定属性的类型!集合中的泛型信息,我们使用ofType获取--><collection property="students" ofType="Student"><result property="id" column="sid"/><result property="name" column="sname"/><result property="tid" column="tid"/></collection>
</resultMap>

  这个不用写javaType, 因为查询出来本身就是一条一条的信息

在查询嵌套处理时,可以将column 看作是传入参数

查询嵌套处理

<select id="getTeacher03" resultMap="TeacherStudent02">select * from Teacher where Id=#{uid1};</select><resultMap id="TeacherStudent02" type="Teacher02"><collection property="student02List" javaType="ArrayList" ofType="Student02" column="Id" select="getStudentByTid"/></resultMap><select id="getStudentByTid" resultType="Student02">select * from Student where Tid=#{tid};</select>

小结

  1. 关联 – association 【多对一】

  2. 集合 – collection 【一对多】

  3. javaType & ofType

    1. JavaType用来指定实体类中的类型

    2. ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型

​ 注意点:

  • 保证SQL的可读性,尽量保证通俗易懂

  • 注意一对多和多对一,属性名和字段的问题

  • 如果问题不好排查错误,可以使用日志,建议使用Log4j

面试高频

  • Mysql引擎

  • InnoDB底层原理

  • 索引

  • 索引优化

12、动态SQL

什么是动态SQL:动态SQL就是根据不同的条件生成不同的SQL语句

所谓的动态SQL,本质上还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码


动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

 IF语句

 where语句

前面几个例子已经方便地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 “state = ‘ACTIVE’” 设置成动态条件,看看会发生什么。

<select id="findActiveBlogLike"resultType="Blog">SELECT * FROM BLOGWHERE<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>

如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:

SELECT * FROM BLOG
WHERE

这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:

SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’

这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。

但是会导致一些问题,比如第一个条件不成立,只有第二个条件成立,这个时候就变成了 SELECT * FROM BLOG WHERE AND title like ‘someTitle’

使用where标签解决这个问题

MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:

<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></where>
</select>

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

 choose语句

<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>

最好在choose里面加上and防止出错

set语句

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

<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>

这个例子中,set 元素会动态地在行首插入 SET 关键字,

并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。 

SQL片段

有的时候,我们可能会将一些功能的部分抽取出来,方便复用!

有的时候,我们可能会将一些功能的部分抽取出来,方便服用!

  1. 使用SQL标签抽取公共部分可

    <sql id="if-title-author"><if test="title!=null">title = #{title}</if><if test="author!=null">and author = #{author}</if>
    </sql>
    
  2. 在需要使用的地方使用Include标签引用即可

    <select id="queryBlogIF" parameterType="map" resultType="blog">select * from blog<where><include refid="if-title-author"></include></where>
    </select>
    

注意事项:

  • 最好基于单标来定义SQL片段

  • 不要存在where标签

动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了

建议:

  • 先在Mysql中写出完整的SQL,再对应的去修改成我们的动态SQL实现通用即可

foreach

说白了就是遍历集合用的,open相当于开头,separator 相当于分隔符 close相当于结尾。

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

<select id="selectPostIn" resultType="domain.blog.Post">SELECT *FROM POST PWHERE ID in<foreach item="item" index="index" collection="list"open="(" separator="," close=")">#{item}</foreach>
</select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

至此,我们已经完成了与 XML 配置及映射文件相关的讨论。下一章将详细探讨 Java API,以便你能充分利用已经创建的映射配置。

13、缓存

13.1 简介

查询 : 连接数据库,耗资源

​ 一次查询的结果,给他暂存一个可以直接取到的地方 –> 内存:缓存

我们再次查询的相同数据的时候,直接走缓存,不走数据库了

  1. 什么是缓存[Cache]?

    • 存在内存中的临时数据

    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题

  2. 为什么使用缓存?

    • 减少和数据库的交互次数,减少系统开销,提高系统效率

  3. 什么样的数据可以使用缓存?

    • 经常查询并且不经常改变的数据 【可以使用缓存】

13.3 一级缓存

  • 一级缓存也叫本地缓存:SqlSession

    • 与数据库同一次会话期间查询到的数据会放在本地缓存中

    • 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库

测试代码

@Testpublic void test2(){SqlSession sqlSession=MybatisUtil.getSqlSession();BlogMapper mapper=sqlSession.getMapper(BlogMapper.class);Blog blog1=mapper.queryBlogById("2587f90269714b6aa78aaeb2a8a1b492");Blog blog2=mapper.queryBlogById("2587f90269714b6aa78aaeb2a8a1b492");System.out.println(blog1);System.out.println(blog2);System.out.println(blog1==blog2);}

测试结果 

这是一个sqlsession过程

缓存失效的情况:

  1. 查询不同的东西

  2. 增删改操作,可能会改变原来的数据,所以必定会刷新缓存

  3. 查询不同的Mapper.xml

  4. 手动清理缓存

    sqlSession.clearCache();

小结:一级缓存默认是开启的,只在一次Sqlsession中有效,也就是拿到链接到关闭链接这个区间段 ,一级缓存相当于一个map

13.4 二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存

  • 工作机制

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中

    • 如果会话关闭了,这个会员对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中

    • 新的会话查询信息,就可以从二级缓存中获取内容

    • 不同的mapper查询出的数据会放在自己对应的缓存(map)中

一级缓存开启(SqlSession级别的缓存,也称为本地缓存)

  • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。

  • 为了提高可扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来定义二级缓存。

步骤:

第一在配置文件中

<!--显示的开启全局缓存-->
<setting name="cacheEnabled" value="true"/>

第二在xml文件中

<!--在当前Mapper.xml中使用二级缓存-->
<cacheeviction="FIFO"flushInterval="60000"size="512"readOnly="true"/>

测试:

未开启二级缓存的时候

只有在一级缓存执行结束之后,才会将信息缓存到二级缓存中,

@Testpublic void test3(){SqlSession sqlSession=MybatisUtil.getSqlSession();BlogMapper mapper=sqlSession.getMapper(BlogMapper.class);Blog blog=mapper.queryBlogById("2587f90269714b6aa78aaeb2a8a1b492");System.out.println(blog);sqlSession.close();SqlSession sqlSession1=MybatisUtil.getSqlSession();BlogMapper mapper1=sqlSession1.getMapper(BlogMapper.class);Blog blog1=mapper1.queryBlogById("2587f90269714b6aa78aaeb2a8a1b492");System.out.println(blog1);sqlSession1.close();}

小结:

  • 只要开启了二级缓存,在同一个Mapper下就有效

  • 所有的数据都会放在一级缓存中

  • 只有当前会话提交,或者关闭的时候,才会提交到二级缓存中

13.5 缓存原理

在这里插入图片描述

13.6 自定义缓存-ehcache

Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存

导包

<dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-ehcache</artifactId><version>1.2.1</version>
</dependency>

关于动态sql

where解决and问题 set解决,逗号问题

 模糊查询

模糊查询这么写?

  1. Java代码执行的时候,传递通配符% %

    List<User> userList = mapper.getUserLike("%李%");
    
    • 1

  2. 在sql拼接中使用通配符

    select * from user where name like "%"#{value}"%"
    
    • 1

Published by

风君子

独自遨游何稽首 揭天掀地慰生平