Mybatis的环境搭建&Mybatis必修知识

    创建项目是的基本的环境可以参考链接对应的文章。JavaWeb之环境搭建_Ostkakah的博客-CSDN博客

Mybatis的环境搭建

mybatis.config.xml配置(在resources目录中),创建对应的jdbc.properties,和对应的mapper。

<?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>
<!--    对POJO中的类进行重命名-->
<!--    可以以包为整体,也可以以某个类为整体-->
    <properties resource="jdbc.properties"></properties>
    <typeAliases>
        <package name="com.huang.POJO"/>
    </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,我们需要保证mapper.xml和mapper接口要保证相同的路径,且mapper.xml要和mapper接口要同名-->
        <package name="com.huang.mappers"/>
    </mappers>
</configuration>

设置log4f.xml文件(固定的,且在resources目录中)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
        </layout>
    </appender>
    <logger name="java.sql">
        <level value="debug" />
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info" />
    </logger>
    <root>
        <level value="debug" />
        <appender-ref ref="STDOUT" />
    </root>
</log4j:configuration>

mybatis启动工具类

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 86159
 * mybatis启动工具
 */
public class mybatisUtil {
    public static SqlSession getSqlSession(String configPath) {
        SqlSession sqlSession = null;
        try {
            InputStream resourceAsStream = Resources.getResourceAsStream(configPath);
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
            sqlSession = build.openSession(true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sqlSession;
    }
}

 Mybatis必修知识

 mybatis和Spring 配合使用所需要的基本依赖为下:

<dependencies>
<!--    测试依赖-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<!--    Spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.9</version>
    </dependency>
<!--    注解依赖-->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.16.18</version>
      <scope>provided</scope>
    </dependency>
<!--    aop依赖-->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.4</version>
    </dependency>
<!--    mysql依赖-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>
<!--    mybatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>
<!--    Spring连接数据库的依赖-->
<!--    Spring链接数据库需要Spring-JDBC-->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.3.10</version>
    </dependency>
<!--    mybatis和Spring整合的包-->
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.6</version>
    </dependency>

  </dependencies>

 mybatis-config.xml的Mapper绑定方法:

1.resources方式(XXX.xml文件可以直接放在resources目录下面)

 <mappers>
      <mapper resource="XXXX.xml"/> 
    </mappers>

2.class方式(XXX.xml要和XXX.java接口的名字相同,且在同个包下,否则会报错)

 <mappers>
        <mapper class="对应的类"/>
    </mappers>

3.package方式

<mappers>
        <package name="包名"/>
    </mappers>

Mybatis-spring的使用

使用Spring的"驱动程序管理器数据源"替换Mybatis的配置, 我们这里使用Spring的jdbc。

下例作为dao层(只要数据源固定,那么我们都不需要对下列进行修改)

流程:

1.编写数据源配置。

2.sqlSessionFactory。

3.sqlSessionTemplate。

4.需要给接口加实现类。

5.将自己的实现类注入到spring中。

6.测试使用即可。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--        DataSource:使用Spring的"驱动程序管理器数据源"替换Mybatis的配置, 我们这里使用Spring的jdbc-->
<!--    dataSource为数据源-->
<!--    设置jdbc-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value=""/>
    </bean>
<!--    sqlSessionFactory:-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--        这里jdbc和mybatis的其他配置要分开写-->
<!--       jdbc-->
    <property name="dataSource" ref="dataSource"/>
<!--        绑定Mybatis配置文件 mybatis-config.xml-->
<!--        configLocation:mybatis配置的地址-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--        绑定Mapper-->
        <property name="mapperLocations" value="classpath:com/huang/Mapper/*.xml"/>
    </bean>
<!--    org.mybatis.spring.SqlSessionTemplate就是我们要使用的sqlSession-->
<!--        创建一个SqlSessionTemplate,其效果和SqlSession的效果相同,最终SqlSessionTemplate会被赋给实现类的属性-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--    只能使用构造器注入,因为它没有set方法-->
<!--        index = 0应该是无参构造-->
        <!--        这里就类似 SqlSession session = build.openSession(true);-->
<!--        相当于openSession的过程-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
<!--    这里的bean类似session.getMapper(UserMapper.class);-->
    <bean id="userMapper" class="com.huang.Mapper.UserMapperimpl">
<!--        为UseMapper设置sqlSession属性-->
        <property name="sqlSession" ref="sqlSession"/>
    </bean>

(重点)对应的UserMapperimpl的实现类一为下:

public class UserMapperimpl implements UserMapper{
//    我们的所有操作都使用sqlSession来执行在原来时候,现在使用SqlSessinTemplate;
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

(理解即可)对应的UserMapperimpl的实现类二为下:

重点是继承SqlSessionDaoSupport,我们才能使用getSqlSession。

public class UserMapperimpl2 extends SqlSessionDaoSupport implements UserMapper{

    @Override
    public List<User> selectUser() {
        //SqlSessionDaoSupport类似工厂
        SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
//        == return getSqlSession().getMapper(UserMapper.class).selectUser();
    }
}

且在spring的配置文件中注册该类。对应的代码如下:

<!--    不需要注入属性-->
    <bean id="UserMapper2" class="com.huang.Mapper.UserMapperimpl2"></bean>

有了这个xml配置后,我们就可以就application-config.xml中的<environments>标签中的内容删除了,因为标签中的内容是Mybatis连接数据库,现在由Spring完成了。

强调:一定要导入Spring-jdbc的依赖

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.3.10</version>
    </dependency>

且要符合对应的版本条件。

至此mybatis-config.xml中的JDBC就要全部删除,在该xml文件中我们只在zmybatis的配置文件中放别名和设置(如:日志设置)。

XXXTemplate:是一种模板,会很经常遇到。

如果存在多人开发beans.xml,我们就可以使用<import>就所有beans.xml,导入到applicationContest.xml。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
          <!--这里设置了一个日志实例,该日志为STDOUT_LOGGING-->
    </settings>
    <import resource="XXXXX.xml"/>
    <import resource="XXXXX.xml"/>
    <import resource="XXXXX.xml"/>
</beans>

声明式事务

  • 要么都成功,要么都失败。
  • 事务在项目开发中,十分的重要,涉及到数据一致性的问题。
  • 确保完整性和一致性。 

事务的特性:ACID

  • 原子性:要么都成功,要么都失败。
  • 一致性:总量不会发生改变。
  • 持久性:事务一但提交就会持久化到数据库,无论系统发生了什么问题。
  • 隔离性:多个业务可以操作同一个资源,防止数据损坏。

spring中的事务分为:

  • 声明式事务:AOP
  • 编程式事务:需要在代码中进行事务的管理。(通过try/catch捕获异常,进行回滚, 不符合开闭原则,不推荐使用)

使用声明式事务的步骤:

  1. 配置声明式事务
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--       第一种写法 <constructor-arg ref="dataSource"/>-->
<!--        第二种写法-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

     2.结合AOP实现事务的织入

方法一:在spring-dao.xml中的sqlSessionFactory中添加一个参数:

<property name="transactionFactory">
            <bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory"/>
        </property>

方法二:配置事务的通知

<!--    结合AOP实现事务的织入-->
<!--    配置事务的通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--        给那些方法配置事务-->
<!--        配置事务的传播特性 新东西-->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
<!--            查询的话就不需要提交事务,所以可以设为只读-->
            <tx:method name="query" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
<!--    配置事务的切入-->
<!--    设置切入点-->
    <aop:config>
<!--        第一*:表示com.huang下的所有包-->
<!--        第二个*:表示com.huang下的所有类-->
<!--        第三个*:表示com.haung.*下的所有方法-->
        <aop:pointcut id="txPointCut" expression="execution(* com.huang.Mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>

propagation的七种配置

    

    Mybatis-plus

对应的依赖:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.3.1.tmp</version>
</dependency>

对应的例子为下:

@Mapper
@Repository
public interface UserMapper extends BaseMapper<User> {
}

其基于BaseMapper类实现,其需要一个泛型,该泛型代表返回类型或参数类型。

    配置mybatis-plus的日志

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

日志的实现类有很多种,例如:log4j,StdOut等等。

日志可以显示执行的sql。

 继承ServiceImpl实现简单的Service层业务

例子:

service接口:

package com.huang.Service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.huang.pojo.User;

public interface UserService extends IService<User> {
}

serviceImpl:

package com.huang.Service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huang.mapper.UserMapper;
import com.huang.pojo.User;
import org.springframework.stereotype.Service;

//继承ServiceImpl,其中的泛型表示对应的mapper和对应的实体类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService{
}

 最终就可以调用其方法:

表明和实体类名不一致的情况

使用@TableName解决

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

 id的设置: 在执行插入语句时,我们不需要set对应的id,BaseMapper回自动生成一个id,其id的生成使用雪花算法。

雪花算法,是 Twitter 开源的分布式 id 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id。在分布式系统中的应用十分广泛,且ID 引入了时间戳,基本上保持自增的。

设置id的属性:

@TableId(type = IdType.AUTO)//表示自增
@TableId(type = IdType.ID_WORKER)//表示全局唯一id
@TableId(type = IdType.UUID)//使用UUID
@TableId(type = IdType.NONE)//不设置
@TableId(type = IdType.INPUT)//手动设置
@TableId("value")//value属性可以设置主键属性在数据库表中对应主键的字段,可以防止主键属性和主键字段名冲突

mybatis-plus解决了动态拼接sql,大大提高了编码效率。

设置全局的主键生成策略

 通过application.yml配置文件配置全局的主键生成策略。

mybatis-plus:
  global-config:
    db-config:
      id-type: 

设置实体类属性在表中对应的字段名

 使用@TableField。

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {
    @TableId
    private Long id;
//    设置此属性在表中对应的值
    @TableField("name")
    private String name;
    @TableField("age")
    private Integer age;
    @TableField("email")
    private String email;
    //设置为逻辑删除字段
    @TableLogic
    private Integer isDeleted;

    public User(Long id, String name, Integer age, String email) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.email = email;
    }
}

 使用枚举类

使用枚举类可以提高可读性,便于维护。

创建枚举类

import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;

@Getter
public enum sexEnum {
    MALE(1, "男"),
    FEMALE(0, "女");

    @EnumValue//保证传入的数据库的数据是sexId
    private Integer sexId;
    private String sexName;

    sexEnum(Integer sexId, String sexName) {
        this.sexId = sexId;
        this.sexName = sexName;
    }
}

 进行测试

import com.huang.enums.sexEnum;
import com.huang.mapper.UserMapper;
import com.huang.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class UserTest {

    @Autowired
    UserMapper userMapper;

    @Test
    public void test1() {
        User user = new User();
        user.setName("tolen");
        user.setAge(22);
        user.setEmail("1111");
        user.setSex(sexEnum.MALE);
        System.out.println(user);
        System.out.println(userMapper.insert(user));
    }
}

测试结果为下:

 插入的sex为1。

乐观锁

乐观锁的实现方式:

1. 去出记录时,获取当前的version。

2.更新时,带上这个version。

3.执行更新时,set version = newVersion where version = oldVersion。

4.如果version不对,就更新失败。

在mybatisPlusConfig类中配置乐观锁的拦截器

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.huang.mapper")
public class mybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
//        设置mybatisPlus的分页插件
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//        mybatisPlus可以对多种的数据库进行分页,所以我们要设置为mysql类型
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        //通过乐观锁拦截器
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

在对应的实体类中的对应属性上添加@Version

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;

@Data
@TableName("t_product")
public class Product {
    private Long id;
    private String name;
    private Integer price;
    @Version//将此字段设置为版本号
    private Integer version;
}

模拟场景测试

import com.huang.mapper.ProductMapper;
import com.huang.pojo.Product;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class ProductTest {

    @Autowired
    ProductMapper productMapper;

    @Test
    void test1() {
        //业务场景: A和b同时要修改t_product中的信息,为了保证两人都能修改成功,我们需要使用乐观锁
        //A将价格-50
        //b将价格-30

        //a,b同时查询了数据库中对应的信息
        Product productA = productMapper.selectById(1);
        System.out.println(productA);
        Product productB = productMapper.selectById(1);
        System.out.println(productB);
        //a先进行操作
        productA.setPrice(productA.getPrice() - 50);
        System.out.println(productMapper.updateById(productA));
        //b在进行操作,在此时b应该是操作失败的,因为version发生了改变
        productB.setPrice(productA.getPrice() - 30);
        int result = productMapper.updateById(productB);
        while(result == 0) {
            //重新执行业务
            //因为未来的业务中基本都是多线程的环境,
            //所以我们不能保证一次就能完成对应的业务,需要修改到成功为止
            productB = productMapper.selectById(1);
            productB.setPrice(productB.getPrice() - 30);
            //此时模拟一次就会成功,就会直接跳出循环
            result = productMapper.updateById(productB);
        }
    }
}

测试结果为下

 数据库结果

 

    逻辑删除

例子:图片回收站,在表中创建一个isDeleted字段,当delete为1时就可以查看图片,当isDeleted为0时就不可以查看图片,在数据库中其实未对数据进行真正的删除。

1.在对应的实体类中添加对应的delete属性添加@TableLogic注解。

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {
    @TableId
    private Long id;
//    设置此属性在表中对应的值
    @TableField("name")
    private String name;
    @TableField("age")
    private Integer age;
    @TableField("email")
    private String email;
    //设置为逻辑删除字段
    @TableLogic
    private Integer isDeleted;

    public User(Long id, String name, Integer age, String email) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.email = email;
    }
}

3.在application中配置逻辑删除

mybatis-plus:
  global-config:
    db-config:
#      删除时为1
      logic-delete-value: 1
#      未删除时为0
      logic-not-delete-value: 0
#且默认中未删除未0,删除为1

 进行测试,测试结果为下:

 最终执行update操作,将isDeleted的值赋为1,如果属性中存在@TableLogic的话,每次查询时都会在sql语句中的后面添加isDeleted不为1的条件,最终查询出数据。

    性能分析插件

导入插件:

 @Bean
    @Profile({"dev","test"})//对应的环境
    public PerformanceInterceptor performanceInterceptor(){
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        performanceInterceptor.setMaxTime(1);//最大执行时间为1毫秒,如果超过了时间,其就会停止
        performanceInterceptor.setFormat(true);//开起格式化支持
        return performanceInterceptor;
    }

    Wrapper(条件构造器)

public class WrapperTest {
    @Autowired
    UserMapper userMapper;
    @Test
    public void selectTest1(){
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.isNotNull("name");
        queryWrapper.isNotNull("email");
        queryWrapper.ge("age",12);
        userMapper.selectList(queryWrapper);
    }
    @Test
    public void selectOne(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name", "黄飞文");
        userMapper.selectOne(queryWrapper);
    }
    @Test
    public void selectBetween(){
        //查询区间
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.between("age", 20, 30);
//        Integer integer = userMapper.selectCount(queryWrapper);
        List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
        maps.forEach(System.out::println);
    }
    @Test
    //模糊查询
    public void SelectBetween2(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //左和右 %e,e%
        queryWrapper.likeRight("email","test");//左查询
        queryWrapper.likeLeft("name","黄");//右查询
//        queryWrapper.inSql("id", "select id from user where id < 3"),insql(可用于多表查询)
        List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
        maps.forEach(System.out::println);
    }
    @Test
    public void OrderBy(){
//        排序
        QueryWrapper<User> qWrapper = new QueryWrapper<>();
//        qWrapper.orderByAsc("id");
        qWrapper.orderByDesc("id");
        List<User> users = userMapper.selectList(qWrapper);
        users.forEach(System.out::println);
    }
}

配置多数据源 

引入依赖

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>

 设置application.yml配置文件

spring:
  datasource:
    dynamic:
      #默认的主数据库名字就叫master
      primary: master
      # 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源就是master
      strict: false
      datasource:
        #设置master数据库的相关信息
        master:
          url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=true
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password:
        #设置从库的信息,从库的名字是自定义的(slave_1)
        slave_1:
          url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncoding=utf-8&useSSL=true
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password:

创建俩个数据库的service层,@Ds中的value值就是指定访问的数据库的名字,这里的数据库的名字必须为在application.yml中设置的数据库名相同,@Ds不仅可以作用在类上,也可以作用在方法上,便于我们在同一个类service类中实现相同表的读写分离操作。

//对应的service接口就是继承IService<Product>
package com.huang.Service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huang.Service.ProductService;
import com.huang.mapper.ProductMapper;
import com.huang.pojo.Product;
import org.springframework.stereotype.Service;

@Service
@DS("slave_1")
//@Ds中的value值就是指定访问的数据库的名字,这里的数据库的名字必须为在application.yml中设置的数据库名相同
//@Ds不仅可以作用在类上,也可以作用在方法上,便于我们在同一个类service类中实现相同表的读写分离操作
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
}
对应的Serice接口就是继承ISerice<User>
package com.huang.Service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huang.Service.UserService;
import com.huang.mapper.UserMapper;
import com.huang.pojo.User;
import org.springframework.stereotype.Service;

@Service
@DS("master")
//@Ds中的value值就是指定访问的数据库的名字,这里的数据库的名字必须为在application.yml中设置的数据库名相同
//@Ds不仅可以作用在类上,也可以作用在方法上,便于我们在同一个类service类中实现相同表的读写分离操作
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

进行测试

@SpringBootTest
class Mybatisplus1ApplicationTests {

    @Autowired
    ProductService productService;
    @Autowired
    UserService userService;

    @Test
    void test1() {
        //slave_1数据库中的信息
        System.out.println(productService.getById(1));
        //master数据库中的信息
        System.out.println(userService.getById(1));
    }

}

 测试结果为下

mybatis-X

代码生成器

 通过连接数据库,右击对应的多个或一个表,执行mybatis-x的代码生成。

最终结果

条件的优先级

 例子:使用lambda表达式表示优先级

    @Test
    void test7() {
        //        设置条件的优先级
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        //名字为yyy1并且age=21或email=null
        QueryWrapper<User> and = userQueryWrapper.eq("name", "yyy1").and(i -> i.eq("age", 21).or().isNull("email"));
        List<User> users = userMapper.selectList(and);
    }

在条件构造器中使用select方法

 当我们需要的数据不是全部字段时,我们可以通过条件构造器设置需要查询的字段。

例子:

   @Test
    void test8() {
//        当我们需要的数据不是全部字段时,我们可以通过条件构造器设置需要查询的字段
//        在条件构造器中使用select方法进行操作,使用selectMaps进行查询
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.select("name", "age");
        List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
        maps.forEach(System.out::println);
    }

在条件构造器中使用inSql方法

 使用子查询操作查询数据

例子:

 @Test
    void test9() {
        //使用子查询操作查询数据
        //这里的测试例子:将id<10的数据的使用信息打印出来
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.inSql("id", "select id from user where id < 10");
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(user -> System.out.println(user));
    }

 使用condition参数可以先进行判断数据的真实性

例子:

 @Test
    void test10() {
        //在用户传来条件时,可能某些字段是空的,所以我们需要进行判断
        //使用condition先对应数据进行判空,仔进行条件构造
        String name = "a";
        Integer ageStart = 18;
        Integer ageEnd = 25;
        String email = "";
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.like(StringUtils.isNotBlank(name), "name", name)
                .ge(ageStart != null, "age", ageStart)
                .le(ageEnd != null, "age", ageEnd)
                .eq(StringUtils.isNotBlank(email), "email", email);
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(user -> System.out.println(user));
    }

LambdaQueryWrapper

为了避免我们在使用条件构造器时将数据库中的字段名写错,我们可以使用Lambda表达式获取数据库中对应的字段名。(通过get方法获取数据库中的字段名)

例子:

 @Test
    void test11() {
        //使用LambdaQueryWrapper
        //在用户传来条件时,可能某些字段是空的,所以我们需要进行判断
        //使用condition先对应数据进行判空,仔进行条件构造
        String name = "a";
        Integer ageStart = 18;
        Integer ageEnd = 25;
        String email = "";
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StringUtils.isNotBlank("name"), User::getName, name)
                .ge(ageStart != null, User::getAge, ageStart)
                .le(ageEnd != null, User::getAge, ageEnd)
                .like(StringUtils.isNotBlank(email), User::getEmail, email);
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(user -> System.out.println(user));
    }

myabtisPlus分页插件

 在mybatisPlusConfig类中进行配置

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.huang.mapper")
public class mybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
//        设置mybatisPlus的分页插件
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//        mybatisPlus可以对多种的数据库进行分页,所以我们要设置为mysql类型
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }
}

测试

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.huang.mapper.UserMapper;
import com.huang.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class MybatisPlusPluginTest {

    @Autowired
    UserMapper userMapper;

    @Test
    void test1() {
//        设置current当前页码,size表示每页对应的条数
        Page<User> page = new Page<>(3, 3);
//        打印使用的信息,并分页
        Page<User> data = userMapper.selectPage(page, null);
//        在使用selectPage方法后,最终的值会封装给page,所以继续使用page进行后续操作
        System.out.println(page);
        System.out.println(page.getTotal());
        System.out.println(page.hasNext());
        System.out.println(page.hasPrevious());
        System.out.println(page.getCurrent());
        System.out.println(page.getSize());
    }
}

 测试结果为下:

xml自定义分页

 在mapper接口中编写方法(要使用mybatisPlus的分页插件就要保证方法的第一个参数为Page,方法的额返回值为Page<T>)

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.huang.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface UserMapper extends BaseMapper<User> {

    Page<User> getListByAge(@Param("page") Page page, @Param("age") Integer age);
}

在mapper.xml中编写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.huang.mapper.UserMapper">
        <select id="getListByAge" resultType="com.huang.pojo.User">
            select * from user
            <where>
                <if test="age != null">
                    age = #{age}
                </if>
            </where>
        </select>
</mapper>

 使用方法测试

  @Test
    void test2() {
        Page<User> page = new Page<>(1, 3);
        userMapper.getListByAge(page, 21);
        System.out.println(page.getTotal());
    }

测试结果为下:

代码自动生成器

添加依赖:

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>

创建测试类

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.junit.jupiter.api.Test;

import java.util.Collections;

public class FastAutoGeneratorTest {
    @Test
    void test1() {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=UTF-8&useSSL=true", "root", "")
                        .globalConfig(builder -> {
                            builder.author("tolen") // 设置作者
//.enableSwagger() // 开启 swagger 模式
                                    .fileOverride() // 覆盖已生成文件
                                    .outputDir("D://mybatis_plus"); // 指定输出目录
                        })
                        .packageConfig(builder -> {
                            builder.parent("com.huang") // 设置父包名
                                    .moduleName("mybatisplus") // 设置父包模块名
                                    .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus"));
// 设置mapperXml生成路径
                        })
                        .strategyConfig(builder -> {
                            builder.addInclude("user","t_product") // 设置需要生成的表名
                                    .addTablePrefix("t_"); // 设置过滤表前缀
                        })
                        .templateEngine(new FreemarkerTemplateEngine())
    // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                        .execute();
    }
}

结果为下:

   

@MapKey(“id”)

在对应的mapper的接口上添加此直接,就是将原本返回List的数据 ,以对应的id为键,以每一条数据为值,返回一个Map<String, object>。

Mybatis获取添加属性中自增的主键

  • 在mapper.xml中设置两个属性

  • useGeneratedKeys:设置使用自增的主键

    • keyProperty:因为增删改有统一的返回值是受影响的行数,因此只能将获取的自增的主键放在传输的参数user对象的某个属性中

对应的代码例子

<?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.huang.mappers.UserMapper">
<!--    useGeneratedKeys="true" 设置使用自增的主键-->
<!--    keyProperty="id"将主键放入传入类中的对应属性上-->
    <insert id="saveAndReturnKey" useGeneratedKeys="true" keyProperty="id">
        insert into user values (#{id}, #{name}, #{password})
    </insert>
</mapper>

 结果为下:

自动生成的主键被赋给了传入的实体类。 

mysql和java名命冲突问题 

方法一:在mybatis-config.xml中的setting中设置mapUnderscoreToCamelCase为true。

 代码为下:

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

    <settings>
<!--        开启数据库命名规则和java命名规则的转换-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    
</configuration>

 方法二:设置结果映射(resultMap)

将每个字段进行自定义的映射(每个字段必须都设置,即使名字一样)

代码为下:

<?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.huang.mappers.employeeMapper">

    <!--    type表示返回的类型-->
    <resultMap id="employeeResultMap" type="com.huang.pojo.Employee">
        <!--        设置主键的映射关系-->
        <id property="id" column="id"></id>
        <result property="empName" column="emp_name"></result>
        <result property="empSex" column="emp_sex"></result>
        <result property="empEmail" column="emp_email"></result>
        <result property="comId" column="com_id"></result>
    </resultMap>

    <select id="getAllEmployee" resultMap="employeeResultMap">
        select * from t_emp;
    </select>

</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">
<mapper namespace="com.huang.mappers.employeeMapper">

    <resultMap id="Employee" type="com.huang.pojo.Employee">
        <id property="id" column="id"></id>
        <result property="empName" column="emp_name"></result>
        <result property="empSex" column="emp_sex"></result>
        <result property="empEmail" column="emp_email"></result>
        <result property="comId" column="com_id"></result>
        <result property="company.comId" column="com_id"></result>
        <result property="company.comName" column="com_name"></result>
    </resultMap>
    <select id="getOneByIdEmployee" resultMap="Employee">
    select * from t_emp left join company on t_emp.id = company.com_id where id = #{id}
    </select>

</mapper>

 方案二:使用association处理映射关系

<?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.huang.mappers.employeeMapper">

<!--    2.使用association处理映射关系-->
    <resultMap id="Employee" type="com.huang.pojo.Employee">
        <id property="id" column="id"></id>
        <result property="empName" column="emp_name"></result>
        <result property="empSex" column="emp_sex"></result>
        <result property="empEmail" column="emp_email"></result>
        <result property="comId" column="com_id"></result>
        <association property="company" javaType="com.huang.pojo.Company">
<!--            调用association标签,通过javaType指定要反射的对象,将数据库的信息赋值给该对象的各个属性,
                最终将此对象赋给association标签中property对应的属性-->
            <result property="comId" column="com_id"></result>
            <result property="comName" column="com_name"></result>
        </association>
    </resultMap>

    <select id="getOneByIdEmployee" resultMap="Employee">
    select * from t_emp left join company on t_emp.id = company.com_id where id = #{id}
    </select>

</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">
<mapper namespace="com.huang.mappers.employeeMapper">

<!--    3.使用分步查询, 最为常用的方法,到后期分布式的时候由于数据量巨大,我们要减少使用连表查询的操作,使用单表操作,加快速度-->
<!--    此方法可以提高代码的利用率和提高可读性-->
    <resultMap id="Employee" type="com.huang.pojo.Employee">
            <id property="id" column="id"></id>
            <result property="empName" column="emp_name"></result>
            <result property="empSex" column="emp_sex"></result>
            <result property="empEmail" column="emp_email"></result>
            <result property="comId" column="com_id"></result>
<!--        select属性要使用唯一标识,这里就可以使用对应方法的全路径-->
            <association property="company"
                         column="com_id"
                         select="com.huang.mappers.companyMapper.getCompanyById">
            </association>
<!--        column属性就是作为后续select的条件-->
<!--        最后将查询得到的数据赋值给 association标签中的property中对应的属性-->
        </resultMap>


    <select id="getOneByIdEmployee" resultMap="Employee">
    select * from t_emp where id = #{id}
    </select>

</mapper>

companymapper中的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.huang.mappers.companyMapper">

    <resultMap id="Company" type="com.huang.pojo.Company">
        <id property="comId" column="com_id"></id>
        <result property="comName" column="com_name"></result>
    </resultMap>
        <select id="getCompanyById" resultMap="Company">
            SELECT * FROM company WHERE com_id = #{comId}
        </select>
</mapper>

在后续的一对多处理中尽量使用第三种方法,可以提高运行效率和提高可读性。

多对一的属性赋值方案 

方案一:使用collection标签

<?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.huang.mappers.companyMapper">

    <resultMap id="CompanyAndEmployeeList" type="com.huang.pojo.Company">
        <id property="comId" column="com_id"></id>
        <result property="comName" column="com_name"></result>
        <collection property="employeeList" ofType="com.huang.pojo.Employee">
            <!--            ofType表示此集合的类型,与javaType不同-->
            <id property="id" column="id"></id>
            <result property="empName" column="emp_name"></result>
            <result property="empSex" column="emp_sex"></result>
            <result property="empEmail" column="emp_email"></result>
            <result property="comId" column="com_id"></result>
        </collection>
    </resultMap>

    <select id="getCompanyAndEmployeeListById" resultMap="CompanyAndEmployeeList">
        select * from company left join t_emp te on company.com_id = te.com_id where company.com_id = #{comId}
    </select>
</mapper>

 方案二:使用分步查询

EmployeeByComId的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">
<mapper namespace="com.huang.mappers.employeeMapper">

    <resultMap id="EmployeeByComId" type="com.huang.pojo.Employee">
        <id property="id" column="id"></id>
        <result property="empName" column="emp_name"></result>
        <result property="empEmail" column="emp_email"></result>
        <result property="empSex" column="emp_sex"></result>
        <result property="comId" column="com_id"></result>
    </resultMap>

    <select id="getEmployeeByComId" resultMap="EmployeeByComId">
        select * from t_emp where com_id = #{comId}
    </select>

</mapper>

分布查询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">
<mapper namespace="com.huang.mappers.companyMapper">

    <resultMap id="CompanyAndEmployeeList" type="com.huang.pojo.Company">
        <id property="comId" column="com_id"></id>
        <result property="comName" column="com_name"></result>
        <collection property="employeeList"
                    ofType="com.huang.pojo.Employee"
                    column="com_id"
                    select="com.huang.mappers.employeeMapper.getEmployeeByComId">
        </collection>
    </resultMap>

    <select id="getCompanyAndEmployeeListById" resultMap="CompanyAndEmployeeList">
        select * from company where com_id = #{comId}
    </select>
</mapper>

延迟加载

  • 分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息:

  • lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载

    • aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载(默认是false,所以无需设置)

  • 此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType="lazy(延迟加载)|eager(立即加载)"

 开启延迟加载

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

<!--        开启延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>
    
</configuration>

此时是开启所有的分布查询都进行延迟加载。(就是懒汉式)

有时可能会存在一些分步查询不需要延迟加载此时就可以设置fetchType属性,此属性有eager/lazy两个值,eager就是全部加载(饿汉式),lazy就是延迟加载(懒汉式)。

<?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.huang.mappers.employeeMapper">

<!--    3.使用分步查询, 最为常用的方法,到后期分布式的时候由于数据量巨大,我们要减少使用连表查询的操作,使用单表操作,加快速度-->
<!--    此方法可以提高代码的利用率和提高可读性-->
    <resultMap id="Employee" type="com.huang.pojo.Employee">
            <id property="id" column="id"></id>
            <result property="empName" column="emp_name"></result>
            <result property="empSex" column="emp_sex"></result>
            <result property="empEmail" column="emp_email"></result>
            <result property="comId" column="com_id"></result>
<!--        select属性要使用唯一标识,这里就可以使用对应方法的全路径-->
            <association property="company"
                         column="com_id"
                         select="com.huang.mappers.companyMapper.getCompanyById"
                         fetchType="eager">
            </association>
<!--        column属性就是作为后续select的条件-->
<!--        最后将查询得到的数据赋值给 association标签中的property中对应的属性-->
        </resultMap>


    <select id="getOneByIdEmployee" resultMap="Employee">
    select * from t_emp where id = #{id}
    </select>

</mapper>

SQL动态查询

where标签

    where标签:使用此标签可以动态的生成where,并且会将去除前面重复的and/or去除。(但是不能去除后面的and/or)

例子:

<?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.huang.mappers.DynamicEmployeeMapper">

    <resultMap id="EmployeeMap" type="com.huang.pojo.Employee">
        <id property="id" column="id"></id>
        <result property="empName" column="emp_name"></result>
        <result property="empSex" column="emp_sex"></result>
        <result property="empEmail" column="emp_email"></result>
        <result property="comId" column="com_id"></result>
    </resultMap>
    <select id="DynameicSQLForGetEmployeeByProperties" resultMap="EmployeeMap">
            select * from t_emp
            <where>
                <if test="empName != null and empName != ''">
                    and emp_name = #{empName}
                </if>
                <if test="empSex != null and empSex != ''">
                    and emp_sex = #{empSex}
                </if>
                <if test="empEmail != null and empEmail != ''">
                    and emp_email = #{empEmail}
                </if>
            </where>
    </select>
</mapper>

    tirm标签:

  • trim用于去掉或添加标签中的内容

  • 常用属性

  • prefix:在trim标签中的内容的前面添加某些内容

    • suffix:在trim标签中的内容的后面添加某些内容

    • prefixOverrides:在trim标签中的内容的前面去掉某些内容

    • suffixOverrides:在trim标签中的内容的后面去掉某些内容

可以解决if语句中出现的多余的and/or。

例子:

<?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.huang.mappers.DynamicEmployeeMapper">

    <resultMap id="EmployeeMap" type="com.huang.pojo.Employee">
        <id property="id" column="id"></id>
        <result property="empName" column="emp_name"></result>
        <result property="empSex" column="emp_sex"></result>
        <result property="empEmail" column="emp_email"></result>
        <result property="comId" column="com_id"></result>
    </resultMap>

<!--tirm标签-->
    <select id="DynameicSQLForGetEmployeeByProperties" resultMap="EmployeeMap">
                select * from t_emp
                <trim prefix="where" suffix="" prefixOverrides="" suffixOverrides="and|or">
#                     如果下面的所有条件都不成立的话,tirm中设置的属性全部不生效
                    <if test="empName != null and empName != ''">
                        emp_name = #{empName} and
                    </if>
                    <if test="empSex != null and empSex != ''">
                        emp_sex = #{empSex} and
                    </if>
                    <if test="empEmail != null and empEmail != ''">
                        emp_email = #{empEmail} and
                    </if>
                </trim>
        </select>
</mapper>

chooce,where,otherwise标签

可以将他们理解为if....else,when至少要有一个,otherwise至多只有一个。

例子:

<?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.huang.mappers.DynamicEmployeeMapper">

    <resultMap id="EmployeeMap" type="com.huang.pojo.Employee">
        <id property="id" column="id"></id>
        <result property="empName" column="emp_name"></result>
        <result property="empSex" column="emp_sex"></result>
        <result property="empEmail" column="emp_email"></result>
        <result property="comId" column="com_id"></result>
    </resultMap>

    <select id="DynameicSQLForGetEmployeeByProperties" resultMap="EmployeeMap">
        select * from t_emp
        <where>
#         最多执行一个when,如果when都没有执行就会执行otherwise中的语句
            <choose>
                <when test="empName != null and empName != ''">
                    emp_name = #{empName}
                </when>
                <when test="empSex != null and empSex != ''">
                    emp_sex = #{empSex}
                </when>
                <when test="empEmail != null and empEmail != ''">
                    emp_email = #{empEmail}
                </when>
                <otherwise>
                    1=1
                </otherwise>
            </choose>
        </where>
    </select>
</mapper>

     foreach标签

用于 多次的插入和删除。

插入例子:

<?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.huang.mappers.DynamicEmployeeMapper">

    <insert id="DynameicSQLForInsertEmployeeList">
        insert into t_emp values
        <foreach collection="Employees" item="item" separator=",">
            (null, #{item.empName}, #{item.empSex}, #{item.empEmail}, null)
        </foreach>
    </insert>
</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">
<mapper namespace="com.huang.mappers.DynamicEmployeeMapper">

<!--    in的写法-->
<!--    <delete id="DynameicSQLFordeleteEmployeeByIds">-->
<!--        delete from t_emp where id in-->
<!--        <foreach collection="ids" item="id" open="(" close=")" separator=",">-->
<!--            #{id}-->
<!--        </foreach>-->
<!--    </delete>-->
<!--    or的写法-->
    <delete id="DynameicSQLFordeleteEmployeeByIds">
        delete from t_emp where
        <foreach collection="ids" item="id" separator="or">
            id = #{id}
        </foreach>
    </delete>
</mapper>

    sql片段

  • sql片段,可以记录一段公共sql片段,在使用的地方通过include标签进行引入

  • 声明sql片段:<sql>标签

<sql id="empColumns">eid,emp_name,age,sex,email</sql>
  • 引用sql片段:<include>标签

<!--List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="Emp">
	select <include refid="empColumns"></include> from t_emp
</select>

mybatis缓存

MyBatis的一级缓存 

  • 一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问

  • 使一级缓存失效的四种情况:

1. 不同的SqlSession对应不同的一级缓存  
2. 同一个SqlSession但是查询条件不同
3. 同一个SqlSession两次查询期间执行了任何一次增删改操作
4. 同一个SqlSession两次查询期间手动清空了缓存

MyBatis的二级缓存

  • 二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取

  • 二级缓存开启的条件

1. 在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置
2. 在映射文件中设置标签<cache />
3. 二级缓存必须在SqlSession关闭或提交之后有效
4. 查询的数据所转换的实体类类型必须实现序列化的接口
  • 使二级缓存失效的情况:两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效

  • 二级缓存的相关配置:

  • 在mapper配置文件中添加的cache标签可以设置一些属性

  • eviction属性:缓存回收策略

  • LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。

    • FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。

    • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。

    • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

    • 默认的是 LRU

  • flushInterval属性:刷新间隔,单位毫秒

  • 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句(增删改)时刷新

  • size属性:引用数目,正整数

  • 代表缓存最多可以存储多少个对象,太大容易导致内存溢出

  • readOnly属性:只读,true/false

  • true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。

    • false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false

MyBatis缓存查询的顺序

  • 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用

  • 如果二级缓存没有命中,再查询一级缓存

  • 如果一级缓存也没有命中,则查询数据库

  • SqlSession关闭之后,一级缓存中的数据会写入二级缓存

分页插件

分页插件使用步骤

添加依赖

<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>

配置分页插件

  • 在MyBatis的核心配置文件(mybatis-config.xml)中配置插件

<plugins>
    <!--设置分页插件-->
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

分页插件的使用

开启分页功能

  • 在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)开启分页功能

  • pageNum:当前页的页码

    • pageSize:每页显示的条数

@Test
public void testPageHelper() throws IOException {
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    //访问第一页,每页四条数据
    PageHelper.startPage(1,4);
    List<Emp> emps = mapper.selectByExample(null);
    emps.forEach(System.out::println);
}

分页相关数据

方法一:直接输出

@Test
public void testPageHelper() throws IOException {
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    //访问第一页,每页四条数据
    Page<Object> page = PageHelper.startPage(1, 4);
    List<Emp> emps = mapper.selectByExample(null);
    //在查询到List集合后,打印分页数据
    System.out.println(page);
}
  • 分页相关数据:

```
Page{count=true, pageNum=1, pageSize=4, startRow=0, endRow=4, total=8, pages=2, reasonable=false, pageSizeZero=false}[Emp{eid=1, empName='admin', age=22, sex='男', email='456@qq.com', did=3}, Emp{eid=2, empName='admin2', age=22, sex='男', email='456@qq.com', did=3}, Emp{eid=3, empName='王五', age=12, sex='女', email='123@qq.com', did=3}, Emp{eid=4, empName='赵六', age=32, sex='男', email='123@qq.com', did=1}]
```

方法二使用PageInfo

  • 在查询获取list集合之后,使用PageInfo<T> pageInfo = new PageInfo<>(List<T> list, intnavigatePages)获取分页相关数据

  • list:分页之后的数据

    • navigatePages:导航分页的页码数

@Test
public void testPageHelper() throws IOException {
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    PageHelper.startPage(1, 4);
    List<Emp> emps = mapper.selectByExample(null);
    //5就是导航的目录个数
    PageInfo<Emp> page = new PageInfo<>(emps,5);
    System.out.println(page);
}
  • 分页相关数据:

```
PageInfo{
pageNum=1, pageSize=4, size=4, startRow=1, endRow=4, total=8, pages=2, 
list=Page{count=true, pageNum=1, pageSize=4, startRow=0, endRow=4, total=8, pages=2, reasonable=false, pageSizeZero=false}[Emp{eid=1, empName='admin', age=22, sex='男', email='456@qq.com', did=3}, Emp{eid=2, empName='admin2', age=22, sex='男', email='456@qq.com', did=3}, Emp{eid=3, empName='王五', age=12, sex='女', email='123@qq.com', did=3}, Emp{eid=4, empName='赵六', age=32, sex='男', email='123@qq.com', did=1}], 
prePage=0, nextPage=2, isFirstPage=true, isLastPage=false, hasPreviousPage=false, hasNextPage=true, navigatePages=5, navigateFirstPage=1, navigateLastPage=2, navigatepageNums=[1, 2]}
```
  • 其中list中的数据等同于方法一中直接输出的page数据

常用数据:

  • pageNum:当前页的页码

  • pageSize:每页显示的条数

  • size:当前页显示的真实条数

  • total:总记录数

  • pages:总页数

  • prePage:上一页的页码

  • nextPage:下一页的页码

  • isFirstPage/isLastPage:是否为第一页/最后一页

  • hasPreviousPage/hasNextPage:是否存在上一页/下一页

  • navigatePages:导航分页的页码数

  • navigatepageNums:导航分页的页码,[1,2,3,4,5]

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值