快速入门

SpringBoot+Mybaits

idea新建项目

maven添加依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-actuator</artifactId>
  8. </dependency>
  9. <dependency>
  10. <groupId>com.alibaba</groupId>
  11. <artifactId>druid-spring-boot-starter</artifactId>
  12. <version>1.1.10</version>
  13. </dependency>
  14. <!-- mybatis -->
  15. <dependency>
  16. <groupId>org.mybatis.spring.boot</groupId>
  17. <artifactId>mybatis-spring-boot-starter</artifactId>
  18. </dependency>
  19. <dependency>
  20. <groupId>mysql</groupId>
  21. <artifactId>mysql-connector-java</artifactId>
  22. </dependency>
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-starter-jdbc</artifactId>
  26. </dependency>

添加配置文件

  1. server:
  2. port: 8001
  3. servlet:
  4. context-path: /
  5. spring:
  6. application:
  7. name: cloud-payment-service
  8. datasource:
  9. type: com.alibaba.druid.pool.DruidDataSource
  10. driver-class-name: com.mysql.cj.jdbc.Driver
  11. # driver-class-name: org.gjt.mm.mysql.Driver
  12. url: dbc:mysql://localhost:3306/db_test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
  13. username: root
  14. password: 123456
  15. type: com.alibaba.druid.pool.DruidDataSource
  16. druid:
  17. #初始化大小
  18. initialSize: 5
  19. #最小值
  20. minIdle: 5
  21. #最大值
  22. maxActive: 20
  23. #最大等待时间,配置获取连接等待超时,时间单位都是毫秒ms
  24. maxWait: 60000
  25. #配置间隔多久才进行一次检测,检测需要关闭的空闲连接
  26. timeBetweenEvictionRunsMillis: 60000
  27. #配置一个连接在池中最小生存的时间
  28. minEvictableIdleTimeMillis: 300000
  29. validationQuery: SELECT 1 FROM DUAL
  30. testWhileIdle: true
  31. testOnBorrow: false
  32. testOnReturn: false
  33. poolPreparedStatements: true
  34. # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,
  35. #'wall'用于防火墙,SpringBoot中没有log4j,我改成了log4j2
  36. filters: stat,wall,log4j2
  37. #最大PSCache连接
  38. maxPoolPreparedStatementPerConnectionSize: 20
  39. useGlobalDataSourceStat: true
  40. # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
  41. connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
  42. # 配置StatFilter
  43. web-stat-filter:
  44. #默认为false,设置为true启动
  45. enabled: true
  46. url-pattern: "/*"
  47. exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
  48. #配置StatViewServlet
  49. stat-view-servlet:
  50. url-pattern: "/druid/*"
  51. #允许那些ip
  52. allow: 127.0.0.1
  53. login-username: admin
  54. login-password: 123456
  55. #禁止那些ip
  56. deny: 192.168.1.102
  57. #是否可以重置
  58. reset-enable: true
  59. #启用
  60. enabled: true
  61. mybatis:
  62. mapperLocations: classpath:mapper:/*.xml

添加主启动类

@SpringBootApplication
public class PaymentMicro {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMicro.class,args);
    }
}

添加业务代码

添加测试代码

打包部署

mapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.jcoo.system.mapper.StorageFileMapper" >
  <!--取别名-->
  <typeAlias type="com.jcoo.system.model.StorageFile" alias="AS_StorageFile"/>
  <!-- resultMap定义返回结果map id(常量名称) type(指定类型) -->
  <resultMap id="BaseResultMap" type(指定类型)="AS_StorageFile" >
    <!--id column主键列名   property:对应实体类成员名称   jdbcType:jdbc数据类型 >
    <id column="storage_id" property="storageId" jdbcType="VARCHAR" />
    <!--result column列名   >    
    <result column="storage_name" property="storageName" jdbcType="VARCHAR" />
    <result column="original_name" property="originalName" jdbcType="VARCHAR" />
    <result column="storage_prefix" property="storagePrefix" jdbcType="VARCHAR" />
    <result column="storage_path" property="storagePath" jdbcType="VARCHAR" />
    <result column="storage_fullpath" property="storageFullpath" jdbcType="VARCHAR" />
    <result column="md5" property="md5" jdbcType="VARCHAR" />
    <result column="extension" property="extension" jdbcType="VARCHAR" />
    <result column="type" property="type" jdbcType="VARCHAR" />
    <result column="size" property="size" jdbcType="BIGINT" />
    <result column="attribute" property="attribute" jdbcType="VARCHAR" />
    <result column="status" property="status" jdbcType="VARCHAR" />
    <result column="brief" property="brief" jdbcType="VARCHAR" />
    <result column="owner" property="owner" jdbcType="VARCHAR" />
    <result column="creator" property="creator" jdbcType="VARCHAR" />
    <result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
    <result column="modifier" property="modifier" jdbcType="VARCHAR" />
    <result column="modify_time" property="modifyTime" jdbcType="TIMESTAMP" />
  </resultMap>
  <!-- sql可用来定义可重用的SQL代码段,可以包含在其他语句中-->
  <sql id="Base_Column_List" >
    storage_id, storage_name, original_name, storage_prefix, storage_path, storage_fullpath, 
    md5, extension, type, size, attribute, status, brief, owner, creator, create_time, 
    modifier, modify_time
  </sql>
  <!-- 查询 parameterType:输入参数变量类型-->
  <!-- Mybatis传多个参数 : https://blog.csdn.net/bingospunky/article/details/52031908>-->
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" >
    select 
    <include refid="Base_Column_List" />
    from storage_file
    where storage_id = #{storageId,jdbcType=VARCHAR}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.String" >
    delete from storage_file
    where storage_id = #{storageId,jdbcType=VARCHAR}
  </delete>
  <insert id="insert" parameterType="com.jcoo.system.model.StorageFile" >
    insert into storage_file (storage_id, storage_name, original_name, 
      storage_prefix, storage_path, storage_fullpath, 
      md5, extension, type, 
      size, attribute, status, 
      brief, owner, creator, 
      create_time, modifier, modify_time
      )
    values (#{storageId,jdbcType=VARCHAR}, #{storageName,jdbcType=VARCHAR}, #{originalName,jdbcType=VARCHAR}, 
      #{storagePrefix,jdbcType=VARCHAR}, #{storagePath,jdbcType=VARCHAR}, #{storageFullpath,jdbcType=VARCHAR}, 
      #{md5,jdbcType=VARCHAR}, #{extension,jdbcType=VARCHAR}, #{type,jdbcType=VARCHAR}, 
      #{size,jdbcType=BIGINT}, #{attribute,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR}, 
      #{brief,jdbcType=VARCHAR}, #{owner,jdbcType=VARCHAR}, #{creator,jdbcType=VARCHAR}, 
      #{createTime,jdbcType=TIMESTAMP}, #{modifier,jdbcType=VARCHAR}, #{modifyTime,jdbcType=TIMESTAMP}
      )
  </insert> 

  <update id="updateByPrimaryKeySelective" parameterType="com.jcoo.system.model.StorageFile" >
    update storage_file
    <set >
      <if test="storageName != null" >
        storage_name = #{storageName,jdbcType=VARCHAR},
      </if>
      <if test="originalName != null" >
        original_name = #{originalName,jdbcType=VARCHAR},
      </if>
      <if test="storagePrefix != null" >
        storage_prefix = #{storagePrefix,jdbcType=VARCHAR},
      </if>
      <if test="storagePath != null" >
        storage_path = #{storagePath,jdbcType=VARCHAR},
      </if>
      <if test="storageFullpath != null" >
        storage_fullpath = #{storageFullpath,jdbcType=VARCHAR},
      </if>
      <if test="md5 != null" >
        md5 = #{md5,jdbcType=VARCHAR},
      </if>
      <if test="extension != null" >
        extension = #{extension,jdbcType=VARCHAR},
      </if>
      <if test="type != null" >
        type = #{type,jdbcType=VARCHAR},
      </if>
      <if test="size != null" >
        size = #{size,jdbcType=BIGINT},
      </if>
      <if test="attribute != null" >
        attribute = #{attribute,jdbcType=VARCHAR},
      </if>
      <if test="status != null" >
        status = #{status,jdbcType=VARCHAR},
      </if>
      <if test="brief != null" >
        brief = #{brief,jdbcType=VARCHAR},
      </if>
      <if test="owner != null" >
        owner = #{owner,jdbcType=VARCHAR},
      </if>
      <if test="creator != null" >
        creator = #{creator,jdbcType=VARCHAR},
      </if>
      <if test="createTime != null" >
        create_time = #{createTime,jdbcType=TIMESTAMP},
      </if>
      <if test="modifier != null" >
        modifier = #{modifier,jdbcType=VARCHAR},
      </if>
      <if test="modifyTime != null" >
        modify_time = #{modifyTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    where storage_id = #{storageId,jdbcType=VARCHAR}
  </update>
</mapper>

#{} 与 ${}

{} 会进行预处理
${} 直接拼接,会有sql注入风险

缓存

https://tech.meituan.com/2018/01/19/mybatis-cache.html
MyBatis支持二级缓存

  • 总开关:全局配置(mybatis-config.xml)变量参数 cacheEnabled=true
  • select语句所在的Mapper,配置了 或节点,并且有效
  • select语句的参数 useCache=true
  • Pojo类必须实现序列化接口

MyBatis对二级缓存的支持粒度很细,它会指定某一条查询语句是否使用二级缓存

 <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" useCache="true">

默认情况下并没有开启缓存,当需要开启二级缓存时,需要在SQL映射文件中添加:

映射语句文件中的所有 select 语句将会被缓存。
映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

代码生成器

mybatis-generator:maven插件

<build>
  <plugins>
      <!--mybatis自动生成代码插件-->
      <plugin>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>1.3.2</version>
        <configuration>
          <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
          <!-- 是否覆盖,true表示会替换生成的JAVA文件,false则不覆盖 -->
          <overwrite>true</overwrite>
        </configuration>
        <dependencies>
          <!--mysql驱动包-->
          <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
          </dependency>
        </dependencies>
      </plugin>
    </plugins>
</build>

配置文件:
generatorConfig.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
    <!-- 引入配置文件 -->
    <properties resource="generatorConfig.properties"/>

    <!--    &lt;!&ndash; 指定数据连接驱动jar地址 &ndash;&gt;-->
    <!--    <classPathEntry location="${classPath}" />-->

    <!-- 一个数据库一个context -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <property name="id" value=""/>
        <!-- 注释 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/><!-- 是否取消注释 -->
            <property name="suppressDate" value="false"/> <!-- 是否生成注释时间戳-->
        </commentGenerator>

        <!-- jdbc连接 -->
        <jdbcConnection driverClass="${jdbc_driver}"
                        connectionURL="${jdbc_url}" userId="${jdbc_user}"
                        password="${jdbc_password}"/>

        <!-- 类型转换 -->
        <javaTypeResolver>
            <!-- 是否使用bigDecimal, false可自动转化以下类型(Long, Integer, Short, etc.) -->
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>

        <!-- 生成实体类地址 -->
        <javaModelGenerator targetPackage="${packageOfEntity}" targetProject="${packageOfProject}">
            <!-- 是否在当前路径下新加一层schema,eg:fase路径com.oop.eksp.user.model, true:com.oop.eksp.user.model.[schemaName] -->
            <property name="enableSubPackages" value="false"/>
            <!-- 是否针对string类型的字段在set的时候进行trim调用 -->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!-- 生成mapxml文件 -->
        <sqlMapGenerator targetPackage="${packageOfMapperXML}" targetProject="${packageOfProject}">
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>

        <!-- 生成mapxml对应client,也就是接口dao -->
        <javaClientGenerator targetPackage="${packageOfMapper}" targetProject="${packageOfProject}" type="XMLMAPPER">
            <property name="enableSubPackages" value="false"/>
        </javaClientGenerator>

        <!-- 配置表信息 -->
        <!--
             1,schema:数据库的schema;
             2,catalog:数据库的catalog;
             3,alias:为数据表设置的别名,如果设置了alias,那么生成的所有的SELECT SQL语句中,列名会变成:alias_actualColumnName
             4,domainObjectName:生成的domain类的名字,如果不设置,直接使用表名作为domain类的名字;可以设置为somepck.domainName,那么会自动把domainName类再放到somepck包里面;
             5,enableInsert(默认true):指定是否生成insert语句;
             6,enableSelectByPrimaryKey(默认true):指定是否生成按照主键查询对象的语句(就是getById或get);
             7,enableSelectByExample(默认true):MyBatis3Simple为false,指定是否生成动态查询语句;
             8,enableUpdateByPrimaryKey(默认true):指定是否生成按照主键修改对象的语句(即update);
             9,enableDeleteByPrimaryKey(默认true):指定是否生成按照主键删除对象的语句(即delete);
             10,enableDeleteByExample(默认true):MyBatis3Simple为false,指定是否生成动态删除语句;
             11,enableCountByExample(默认true):MyBatis3Simple为false,指定是否生成动态查询总条数语句(用于分页的总条数查询);
             12,enableUpdateByExample(默认true):MyBatis3Simple为false,指定是否生成动态修改语句(只修改对象中不为空的属性);
             13,modelType:参考context元素的defaultModelType,相当于覆盖;
             14,delimitIdentifiers:参考tableName的解释,注意,默认的delimitIdentifiers是双引号,如果类似MYSQL这样的数据库,使用的是`(反引号,那么还需要设置context的beginningDelimiter和endingDelimiter属性)
             15,delimitAllColumns:设置是否所有生成的SQL中的列名都使用标识符引起来。默认为false,delimitIdentifiers参考context的属性
          -->
        <table tableName="storage_file" domainObjectName="StorageFile"
               enableCountByExample="false"
               enableDeleteByExample="false"
               enableSelectByExample="false"
               enableUpdateByExample="false">
            <!-- schema即为数据库名 tableName为对应的数据库表 domainObjectName是要生成的实体类 enable*ByExample
                是否生成 example类   -->
            <!--            &lt;!&ndash; 忽略列,不生成bean 字段 &ndash;&gt;-->
            <!--            <ignoreColumn column="FRED"/>-->
            <!--            &lt;!&ndash; 指定列的java数据类型 &ndash;&gt;-->
            <!--            <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR"/>-->
        </table>
       <!-- 多个table -->
    </context>
</generatorConfiguration>

generatorConfig.properties

jdbc_driver=com.mysql.jdbc.Driver
jdbc_url=jdbc:mysql://localhost:3306/db_jcoo?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
jdbc_user=root
jdbc_password=123456

packageOfProject=src\\main\\java
packageOfEntity=com.jcoo.system.model
packageOfMapper=com.jcoo.system.mapper
packageOfMapperXML=com.jcoo.system.mapper

探究

Mybatis 与 Maven

Mybatis报错: Invalid bound statement (not found) 错误
在使用maven进行依赖管理时,需要注意mybatis需要的Mapper.xml文件(X)与Mapper.java文件(J)的位置关系

  1. 如果X文件在Resource目录下,需要在Resource目录新建与J所在包相同层级的目录,并配置mapper-locations

    mapper-locations:
     - classpth:com.jcoo.jclab.mapper/*Mapper.xml
    

    image.png
    image.png
    如果X文件在与J所在包相同层级的目录(代码目录)下,需要在pom文件的bulid下加入

    <build>
    <resources>
     <resource>
       <directory>src/main/java</directory><!--所在的目录-->
       <includes>
         <include>**/*.MapperXml</include>
       </includes>
     </resource>
    </resources>
    </build>
    

    image.png
    image.png
    (maven默认只认Resource目录下资源文件)
    这样启动应用时maven构建生成的target下Mapper类和Mapper.xml才在同一路径下
    注意:如果使用第二种方式在使用代码插件时,如果插件的配置文件xml中使用引入properties文件会出现如下错误:image.png,可能在每次生成代码时需要注释resource


image.pngimage.png
这两种情况编译后不在同一路径会有问题(我这儿)

MapperScan

Mybatis报错:xxxMapper that could not be found [Autowired(required=true)] 错误
@MapperScan(“com.jcoo.system.mapper”):扫描包下面的接口类,在编译之后都会生成相应的实现类

为什么说 MyBatis 是半ORM?

说 MyBatis 是 半自动 ORM 最主要的一个原因是,它需要在 XML 或者注解里通过手动或插件生成 SQL,才能完成 SQL 执行结果与对象映射绑定。

参考

  1. mybaits参考文档 https://mybatis.org/mybatis-3/zh/index.html
  2. mybatis plus教程 https://baomidou.com/guide/