Mybatis 框架基本使用指南

Mybatis基础

简介

MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。

MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。

MyBatis 使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的POJOs(普通的 Java对象)映射成数据库中的记录。

每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。SqlSessionFactoryBuilder可以从一个xml配置文件或者一个预定义的配置类的实例获得。

用xml文件构建SqlSessionFactory实例是非常简单的事情。推荐在这个配置中使用类路径资源(classpath resource),但其实可以使用任何Reader实例,包括用文件路径或 file:// 开头的 url 创建的实例。MyBatis有一个实用类----Resources,它有很多方法,可以方便地从类路径及其它位置加载资源。


优点

  • MyBatis封装了JBDC底层访问数据库的细节,使我们程序猿不需要与JDBC API打交道,就可以访问数据库
  • MyBatis简单易学,程序猿直接编写SQL语句,适合于对SQL语句性能要求比较高的项目
  • SQL语句封装在配置文件中,便于统一管理与维护,降低了程序的耦合度
  • SQL代码从程序代码中彻底分离出来,可重用
  • 提供了动态SQL标签,支持编写动态SQL
  • 提供映射标签,支持对象与数据库的ORM字段关系映射

缺点

  • 过于依赖数据库SQL语句,导致数据库移植性差,更换数据库,如果SQL语句有差异,SQL语句工作量大
  • 由于xml里标签id必须唯一,导致DAO中方法不支持方法重载

SpringBoot集成Mybatis

依赖

SpringBoot官方并没有提供Mybatis的启动器,不过Mybatis官网自己实现了

		
		<dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>2.1.3version>
        dependency>
		
        <dependency>
            <groupId>com.github.pagehelpergroupId>
            <artifactId>pagehelper-spring-boot-starterartifactId>
            <version>1.2.3version>
        dependency>       

		
		<dependency>
           <groupId>org.springframework.bootgroupId>
           <artifactId>spring-boot-starter-jdbcartifactId>
		dependency>

默认情况下,插件MyBatis-Spring-Boot-Starter将进行如下配置:

  • 自动检查 SpringBoot 的数据源配置并构建 DataSource 对象

  • 通过 SqlSessionFactoryBean 使用数据源构建并注册 SqlSessionFactory 对象

  • 从 SqlSessionFactory 中创建并注册一个 SqlSessionTemplate 实例,其实就是构建一个 SqlSession 对象

  • 自动扫描使用了注解@Mapper的接口映射器,并将这些映射器与SqlSessionTemplate实例进行关联,同时将它们注册到Spring容器中


常用yaml配置

mybatis:
  # 注册XML映射器,即mapper.xml文件位置。如果没有映射文件,请注释掉
  mapper-locations: classpath:mapper/**.xml
  # 配置Java类型别名包扫描路径。通过该属性可以给包中的类注册别名,注册后在 Mapper 对应的 XML 文件中可以直接使用类名,而不用使用全限定的类名(即 XML 中调用的时候可以不用包含全限包名)
  type-aliases-package: com.test.springbootssm.entity
  configuration:
    # 指定MyBatis所用日志的具体实现(输出sql语句),未指定时将自动查找。
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 开启自动驼峰命名规则(camel case)映射。即从经典数据库列名 A_COLUMN(下划线命名)到经典 Java 属性名 aColumn(驼峰命名)的类似映射
    map-underscore-to-camel-case: true

不太常用的yaml配置

mybatis:
  ## 不常用的配置
  check-config-location: true      	 # 是否检测MyBatis运行参数配置文件
  config-location: classpath:mybatis/mybatis-config.xml		# mybatis配置文件所在路径
  type-handlers-package: test.springboot.handlers         	# 配置类型处理器包名
  executor-type: SIMPLE                                   	# 指定执行器类型
  configuration:
      default-fetch-size: 20
      default-statement-timeout: 30
      lazy-loading-enabled: true 		# 开启延时加载开关
      aggressive-lazy-loading: false 	# 将积极加载改为消极加载(即按需加载),默认值就是false
      lazy-load-trigger-methods: "" 	# 指定触发延迟加载的方法
      cache-enabled: true 				# 打开全局缓存开关(二级环境),默认值就是true
      
#MyBatis使用pageHelper分页
pagehelper:
  helper-dialect: mysql		# 配置使用哪种数据库语言,不配置的话pageHelper也会自动检测
  reasonable: true			# 启用查询合理化。如果pageNum<1,则会查询第一页;如果pageNum>pages,则会查询最后一页
  # 支持通过Mapper接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。
  support-methods-arguments: true

上述配置参数最终是通过mybatis-spring-boot-autoconfigure.jar加载和配置的。


Java方式配置MyBatis运行时参数

MyBatis的运行时参数除了可以在SpringBoot的配置文件中指定,还可以通过Java编码方式设置。实际上就是在Spring容器中注册一个实现了ConfigurationCustomizer接口的Bean。

import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisConfig {
    
    @Bean
    ConfigurationCustomizer mybatisConfigurationCustomizer() {
        return new ConfigurationCustomizer() {
            @Override
            public void customize(org.apache.ibatis.session.Configuration configuration) {
                // 在SpringBoot中以Java编码方式配置MyBatis运行时参数
                configuration.setMapUnderscoreToCamelCase(true);
                configuration.addMappers("com.test.springboot.mapper");
            }
        };
    }
}

Spring集成MyBatis

依赖

        
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatisartifactId>
            <version>${version.mybatis}version>
        dependency>
		
        <dependency>
            <groupId>tk.mybatisgroupId>
            <artifactId>mapperartifactId>
            <version>${version.mybatis.mapper}version>
        dependency>
		
        <dependency>
            <groupId>com.github.pagehelpergroupId>
            <artifactId>pagehelperartifactId>
            <version>${version.pagehelper}version>
        dependency>

        
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatis-springartifactId>
            <version>${version.mybatis.spring}version>
        dependency>

        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-txartifactId>
        dependency>

        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-jdbcartifactId>
        dependency>

通过Java方式注册MyBatis核心组件

通过Java方式在Spring框架中注册MyBatis的核心组件Bean,并且配置声明式事务管理。

(1)在Spring中注册MyBatis的核心组件Bean:SqlSessionFactory,SqlSession,以及Spring的事务管理器。另外,在构建SqlSessionFactory时还可以注册MyBatis的xml映射器。

@Configuration
@EnableTransactionManagement
public class MyBatisSpringConfig implements TransactionManagementConfigurer {
    @Autowired
    private DataSource dataSource;
    
    // 在Spring中注册SqlSessionFactory,在这里可以设置一下参数:
    // 1.设置分页参数
    // 2.配置MyBatis运行时参数
    // 3.注册xml映射器
    @Bean
    public SqlSessionFactory sqlSessionFactory() {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        // 设置数据源
        sqlSessionFactoryBean.setDataSource(dataSource);
        // 设置映射POJO对象包名
        // sqlSessionFactoryBean.setTypeAliasesPackage("org.chench.test.springboot.model");
        
        // 分页插件
        /*PageHelper pageHelper = new PageHelper();
        Properties properties = new Properties();
        properties.setProperty("reasonable", "true");
        properties.setProperty("supportMethodsArguments", "true");
        properties.setProperty("returnPageInfo", "check");
        properties.setProperty("params", "count=countSql");
        pageHelper.setProperties(properties);*/
        //添加插件
        //sqlSessionFactoryBean.setPlugins(new Interceptor[]{pageHelper});
        
        // 配置mybatis运行时参数
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        // 自动将数据库中的下划线转换为驼峰格式
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setDefaultFetchSize(100);
        configuration.setDefaultStatementTimeout(30);
        
        sqlSessionFactoryBean.setConfiguration(configuration);
        
        // 在构建SqlSessionFactory时注册xml映射器
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try {
            sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
            return sqlSessionFactoryBean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    
    /**
     * 注入sqlSession对象
     * @param sqlSessionFactory
     * @return
     */
    @Bean(value = "sqlSession")
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    // Spring事务管理器
    @Bean(value = "transactionManager")
    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return new DataSourceTransactionManager(dataSource);
    }
}

(2)注册MyBatis接口映射器 MyBatis 3支持2种映射器:xml映射器和接口映射器,其中xml映射器可以在构建SqlSessionFactory时进行注册。

@Configuration
@AutoConfigureAfter(MyBatisSpringConfig.class) //注意,由于MapperScannerConfigurer执行的比较早,所以必须有该注解
public class MyBatisMapperScannerConfig {
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        // 设置sqlSessionFactory名
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
        // 设置接口映射器基础包名
        mapperScannerConfigurer.setBasePackage("org.chench.test.springboot.mapper");
        Properties properties = new Properties();
        //properties.setProperty("mappers", "org.chench.test.springboot.mapper");
        properties.setProperty("notEmpty", "false");
        properties.setProperty("IDENTITY", "MYSQL");
        mapperScannerConfigurer.setProperties(properties);
        return mapperScannerConfigurer;
    }
}

MyBatis支持2种类型的映射器:XML映射器和接口映射器,在这里以定义并使用接口映射器为例。

定义接口映射器

@Repository
public interface AccountMapper {
    @Select("select * from account where id = #{id}")
    public Account getAccountById(@Param("id") long id);
}

注意: 在这里可以使用Spring容器的注解@Repository声明MyBatis的接口映射器为一个Bean组件,这样在使用接口映射器时可以直接注入这个接口映射器Bean进行使用。


通过xml方式注册MyBatis核心组件

applicationContext-mybaits.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
			    http://www.springframework.org/schema/beans/spring-beans.xsd
			    http://www.springframework.org/schema/context
			    http://www.springframework.org/schema/context/spring-context.xsd
			    http://www.springframework.org/schema/aop
			    http://www.springframework.org/schema/aop/spring-aop.xsd
			    http://www.springframework.org/schema/tx
			    http://www.springframework.org/schema/tx/spring-tx.xsd">

    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="root">property>
        <property name="password" value="root">property>
        <property name="url" value="jdbc:mysql:///heima23">property>
        <property name="driverClassName" value="com.mysql.jdbc.Driver">property>
    bean>

     
    
    <bean id="sqlSessionfactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource">property>
        <property name="typeAliasesPackage" value="cn.test.domain">property>
    bean>

    
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        
        <property name="basePackage" value="cn.test.dao">property>
    bean>


    
    
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource">property>
    bean>

    
    <tx:advice id="txAdvice">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="update*"/>
            <tx:method name="delete*"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="*">tx:method>
        tx:attributes>
    tx:advice>
    
    
    <aop:config>
        <aop:pointcut id="pt" expression="execution(* cn.test.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt">aop:advisor>
    aop:config>

beans>

xml映射文件

Mybatis 框架基本使用指南_第1张图片


DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.test.springbootssm.mapper.UserMapper">
    
    <select id="findById" parameterType="long" resultType="user">
        select * from tb_user where id=#{id}
    select>
mapper>

配置mapper接口扫描

方式一

给每一个Mapper接口添加@Mapper注解,由Spring来扫描这些注解,完成Mapper接口的动态代理。

@Mapper
public interface UserMapper {
}

方式二

在启动类上添加扫描包注解(若Mapper接口文件位置统一存放,推荐此种方式

这种方式的好处是,不用给每一个Mapper都添加注解。

@SpringBootApplication
@MapperScan("com.test.springbootssm.mapper")
public class Application {
    public static void main(String[] args) {
        // 启动代码
        SpringApplication.run(Application.class, args);
    }
}

Mybatis实现Dao层

接口开发规范

  1. 接口全限定名与映射文件的namespace属性一致
  2. 接口的方法名与statement(子标签)的id属性一致
  3. 接口方法参数类型与statement(子标签)的parameterType属性一致
  4. 接口方法返回值类型与statement(子标签)的resultType属性一致

Mybatis 框架基本使用指南_第2张图片

UserMapper接口

public interface UserMapper {
    public List<User> findAll();
}

UserMapper.xml


DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.test.mapper.UserMapper">

<select id="findAll" resultType="user">
	select * from user
select>

mapper>

Mybatis高级查询

查询结果封装

  • resultType
    如果实体的属性名与数据库表中字段名(或查询字段别名)一致,将查询结果自动封装到实体类中。

    • 如果是单条数据,mybatis直接返回封装好数据的对象
    • 如果是多条数据,mybatis会将封装好的多个对象放入list集合中
  • resutlMap
    如果实体的属性名与数据库表中字段名不一致,使用ResutlMap实现手动映射封装到实体类中

    resutlMap的属性说明 :

    <resultMap id="userResultMap" type="user">
        
        
        
    <id column="uid" property="id">id>  						
    <result column="username" property="username">result>  	
        
        
    

    xml映射文件:

    <resultMap id="userResultMap" type="user">
        <id column="uid" property="id">id>
        <result column="username" property="username">result>
        <result column="bir" property="birthday">result>
        <result column="gender" property="sex">result>
        <result column="address" property="address">result>
    resultMap>
    
    <select id="findAllResultMap" resultMap="userResultMap">
        SELECT id AS uid,username AS NAME,birthday AS bir,sex AS gender ,address FROM USER
    select>
    

多条件查询

  • 方式1:将多个参数封装到一个 pojo对象中,进行对象的传递【推荐】

  • 方式2:使用 @Param 注解声明参数【推荐】

    ​ 缺点:参数过多时不太方便

  • 方式3:xml里sql中使用 #{arg0} #{arg1}… 或 #{param1} #{param2}… 传递参数

    ​ 缺点:可读性差

mapper接口:

// 方式1
public List<User> findByIdAndUsername3(User user);
// 方式2
public List<User> findByIdAndUsername2(@Param("id") Integer id,@Param("username") String username);
// 方式3
public List<User> findByIdAndUsername1(Integer id,String username);

xml文件:


<select id="findByIdAndUsername3" parameterType="user" resultType="user">
    select * from user where id = #{id} and username = #{username}
select>


<select id="findByIdAndUsername2" resultType="user">
    select * from user where id = #{id} and username = #{username}
select>


<select id="findByIdAndUsername1" resultType="user">
    
    select * from user where id = #{param1} and username = #{param2}
select>

模糊查询

  • 方式1:sql中使用 concat函数进行模糊匹配拼接【推荐】

  • 方式2:sql中使用 || (字符串连接符)进行模糊匹配拼接【推荐】

  • 方式3:java中添加模糊匹配

    ​ 缺点:在java中出现了sql通配符,产生了耦合性问题,不太推荐使用

    // java示例
    List<User> list = userDao.findLikeUsername("%王%")
    
  • 方式4:sql中拼接模糊匹配(%#{usenme}%)

    ​ 缺点:只支持mysl5.5以上版本,oracle不支持该写法

  • 方式5:sql中拼接模糊匹配(%${value}%)

    ${} 字符串拼接 ,如果接收简单数据类型 名称只能是:${value}

    ​ 缺点:这种方式的底层使用的是编译对象statement做的查询,会出现sql注入问题,开发不能使用

xml文件:


<select id="findByUsername4" parameterType="string" resultType="user">
    select * from user where username like concat('%', #{username}, '%');
select>


<select id="findByUsername4" parameterType="string" resultType="user">
	select * from user where username like ('%' || #{username} || '%');
select>


<select id="findByUsername1" parameterType="string" resultType="user">
    select * from user where username like #{username}
select>


<select id="findByUsername1" parameterType="string" resultType="user">
    select * from user where username like '%#{username}%'
select>


<select id="findByUsername3" parameterType="string" resultType="user">
    select * from user where username like '%${value}%'
select>

分页查询

需要添加 PageHelper 的依赖

		
        <dependency>
            <groupId>com.github.pagehelpergroupId>
            <artifactId>pagehelper-spring-boot-starterartifactId>
            <version>1.2.3version>
        dependency>
	/**
     * 分页查询所有数据
     * */
    @Override
    public PageInfo<Brand> findPage(Integer page, Integer size) {
        //调用PageHelper的startPage方法,设置当前页、每页条数
        PageHelper.startPage(page, size);
        //调用dao接口方法,查询所有的数据
//        List brandList = brandDao.selectAll();
        Page<Brand> list = brandDao.selectAll();
        //把查到的数据封装到PageInfo中,然后返回,PageInFo中包含了所有的数据,总条数等等
        PageInfo<Brand> pageInfo = new PageInfo<>(list);
        //返回即可
        return pageInfo;
    }

动态传递参数方式:#{} 与 ${}

(1)#{}:占位符

#{} 是预编译处理,MyBatis在处理 #{} 时,它会将sql中的 #{} 替换为 ?,然后调用 PreparedStatement 的set方法来赋值,传入字符串后,会在值两边加上单引号。

(2)${}:拼接符

${} 是字符串替换,在处理是字符串替换,mybaits在处理时,它会将 sql 中的 { } 替换为变量的值,传入的数据不会在两边加上单引号。

注意:使用${ }会导致sql注入,不利于系统的安全性!

SQL注入:就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。常见的有匿名登录(在登录框输入恶意的字符串)、借助异常获取数据库信息等

#{} 与 ${} 的区别:

#{}:底层 PreparedStatement
	1.sql与参数分离,不会出现sql注入问题
	2.sql只需要编译一次
	3.接收普通数据类型,命名:#{随便写}
	4.接收引用数据类型,命名:#{属性名} 
	5.变量替换后,#{} 对应的变量自动加上单引号 ''

${}:底层 Statement
	1.将sql与参数拼接在一起,会出现sql注入问题
	2.每次执行sql语句都会编译一次
	3.接收普通数据类型,命名:'${value}'
	4.接收引用数据类型,命名: '${属性名}'  ;注意:需要使用单引号‘${xxx}5.变量替换后,${} 对应的变量不会加上单引号 ''

必须使用@param注解的四种情况

1、方法有多个参数

原因:当不使用 @Param 注解时,mybatis 是不认识哪个参数叫什么名字的,尽管在接口中定义了参数的名称,mybatis仍然不认识。mybatis默认以接口中参数定义的顺序和SQL语句中的表达式进行映射。

例如:

mapper接口方法:

@Mapper
public interface UserMapper {
    Integer insert(@Param("username") String username, @Param("address") String address);
}

对应的xml:

<insert id="insert" parameterType="org.javaboy.helloboot.bean.User">
    insert into user (username, address) values (#{username}, #{address});
insert>

2、方法参数要取别名

mapper接口方法:

@Mapper
public interface UserMapper {
    Integer insert(@Param("name") String username, @Param("address") String address);
}

对应的xml:

<insert id="insert" parameterType="org.javaboy.helloboot.bean.User">
    insert into user (username, address) values (#{name}, #{address});
insert>

3、XML 中的 SQL 使用了 $ 拼接sql

$会有注入的问题,但是有的时候不得不使用 $ 符号,例如要传入列名或者表名的时候,这个时候必须要添加 @Param 注解

mapper接口方法:

@Mapper
public interface UserMapper {
    List<User> getAllUsers(@Param("order_by")String order_by);
    
    List<User> findAll(@Param("tb_user")String tb_user);
}

对应xml:

<select id="getAllUsers" resultType="org.javaboy.helloboot.bean.User">
    select * from user
    <if test=" order_by !=null and order_by != ''">
        order by ${order_by} desc			
    if>
select>

<select id="findAll" resultType="org.javaboy.helloboot.bean.User">
    select * from ${tb_user}
select>

4、动态 SQL 中使用了参数作为变量

如果在动态 SQL 中使用参数作为变量,那么也需要 @Param 注解,即使你只有一个参数。

例如:

@Mapper
public interface UserMapper {
    List<User> getUserById(@Param("id")Integer id);
}

对应xml:

<select id="getUserById" resultType="org.javaboy.helloboot.bean.User">
    select * from user
    <if test="id != null">
        where id=#{id}
    if>
select>

报错处理

使用 foreach 批量操作,报错 ORA-00911: 无效字符的错误

连接数据库:Oracle

错误写法

	<delete id="deleteEntityAll" parameterType="java.lang.reflect.Array">
        <foreach collection="array" item="item" index="index"  separator=";">
            delete from GKCP_TC_JL  where JH = #{item.jh} and to_char(TCRQ,'YYYY-MM-DD') = #{item.tcrq}
        foreach>
    delete>

这样写生成的SQL语句用 ; 隔开,在sql工具中可以识别,但是mybatis不识别

解决方案:在foreach中增加节点操作符可以解决(Oracle的匿名块)

正确写法

	<delete id="deleteEntityAll" parameterType="java.lang.reflect.Array">
        <foreach collection="array" item="item" index="index" open="begin" close=";end;" separator=";">
            delete from GKCP_TC_JL  where JH = #{item.jh} and to_char(TCRQ,'YYYY-MM-DD') = #{item.tcrq}
        foreach>
    delete>

你可能感兴趣的:(#,Java,持久层,java,spring,mybatis)