一、mybatis的环境搭建
-
引入依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.21</version> </dependency> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.4.0</version> </dependency> </dependencies>
-
配置mapper接口扫描
@SpringBootApplication @MapperScan("com.haierac.mybatis.example.mapper") public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); } }
-
配置数据库连接和Mapper.xml文件位置
spring.application.name=mybatis-example server.port=8889 spring.datasource.url=jdbc:mysql://192.168.100.171:3306/vrv_cloudplatform_project_center?useUnicode=true&characterEncoding=utf8 spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #classpath*:mapper/**/*Mapper.xml mybatis.mapper-locations=classpath*:mapper/*Mapper.xml #开启是否打印sql mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl #别名包 mybatis.type-aliases-package=com.haierac.mybatis.example.pojo #开启驼峰命名识别 mybatis.configuration.map-underscore-to-camel-case=true
classpath
和classpath*
的区别主要在于如何搜索和加载资源。当使用classpath
前缀时,Spring 将从类路径中搜索资源,并在找到第一个匹配的资源后立即停止搜索。而当使用classpath*
前缀时,Spring 将从所有类路径中搜索资源,包括 JAR 文件中的类路径。 -
mybatis代码自动生成插件
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
代码自动生成配置文件
<!DOCTYPE generatorConfiguration PUBLIC
"-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- MyBatis3Simple风格 -->
<context id="simple" targetRuntime="MyBatis3Simple">
<property name="javaFileEncoding" value="UTF-8"/>
<!--生成mapper.xml时覆盖原文件-->
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否。 自动生成注释太啰嗦,可以编码扩展CommentGenerator -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!-- 数据库连接 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://192.168.100.171:3306/vrv_cloudplatform_project_center?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=true&serverTimezone=UTC"
userId="root" password="123456">
<!-- 设置为true就只读取cloudplatform_access_control_center下的表, 否则会优先读取到mysql的user表 -->
<property name="nullCatalogMeansCurrent" value="true"/>
</jdbcConnection>
<!-- 生成PO的包名和位置 -->
<javaModelGenerator targetPackage="com.haierac.mybatis.example.entity" targetProject="src/main/java"/>
<!-- 生成XML映射文件的包名和位置 -->
<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"/>
<!-- 生成Mapper接口的包名和位置 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.haierac.mybatis.example.mapper" targetProject="src/main/java"/>
<!-- 要生成对应表配置 -->
<table tableName="vrv_area" domainObjectName="Area" >
<!-- 自增主键列 -->
<generatedKey column="id" sqlStatement="MYSQL" identity="true"/>
<!-- tinyint映射为Integer -->
<!--<columnOverride column="role" javaType="Integer" jdbcType="TINYINT"/>-->
</table>
</context>
</generatorConfiguration>
二、XML映射文件
1、传参方式
<select
id="selectPerson"
parameterType="int"
parameterMap="deprecated"
resultType="hashmap"
resultMap="personResultMap"
flushCache="false"
useCache="true"
timeout="10"
fetchSize="256"
statementType="PREPARED"
resultSetType="FORWARD_ONLY">
</select>
官网地址:非常用参数介绍
-
Mybatis获取参数的两种方式
-
MyBatis获取参数值的两种方式:${}和#{}
${}的本质就是字符串拼接,#{}的本质就是占位符赋值
${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;
但是#{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号;
#{} 方式近似的 JDBC 代码,
String selectPerson = "SELECT * FROM PERSON WHERE ID=?"; PreparedStatement ps = conn.prepareStatement(selectPerson); ps.setInt(1,id);
-
安全性方面:${}可能会引起sql注入 #{}方式不会;实际开发中建议使用#{}方式
-
-
单个字面量类型的参数
- 基本类型的parameterType可以简写 例如 parameterType=“string”;parameterType=“int”
-
实体类类型的参数
#AreaMapper接口 Area selectByAreaParam(AreaParam areaParam); #AreaMapper.xml <select id="selectByAreaParam" parameterType="com.haierac.mybatis.example.param.AreaParam" resultMap="BaseResultMap"> select * from vrv_area where area_name = #{areaName} and id = #{id} </select>
-
使用@Param标识参数
#AreaMapper接口 Area selectByAreaNameAndId(@Param(value = "areaName") String areaName,@Param(value = "id") Long id); #AreaMapper.xml <select id="selectByAreaNameAndId" resultMap="BaseResultMap"> select * from vrv_area where area_name = #{areaName} and id = #{id} </select>
2、结果映射
-
返回实体类对象
-
resultType=实体类名(若未开启别名配置 则为全类名)
-
如果返回多条结果则报错
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2
#AreaMapper接口
Area selectByAreaParam(AreaParam areaParam);#AreaMapper.xml <select id=“selectByAreaParam” parameterType=“AreaParam” resultType=“Area”>
select * from vrv_area where area_name = #{areaName} and id = #{id} </select>
-
-
返回一个List<Area>结果同上
-
返回一个Map<String,Object>类型
-
结果为字段名+值{area_name=测试, update_time=2023-06-07 15:52:40.0, create_time=2023-06-07 15:52:40.0, project_id=1, level=1, parent_id=1, order_rule=1, machine_num=1, id=1, order_num=1}
#AreaMapper接口 Map<String,Object> getAreaMap(@Param(value = "areaName") String areaName,@Param(value = "id") Long id); #AreaMapper.xml <select id="getAreaMap" resultType="map"> select * from vrv_area where area_name = #{areaName} and id = #{id} </select>
-
-
多条数据返回List<Map<String,Object>>类型
-
方式一
结果:[{area_name=测试, update_time=2023-06-07 15:52:40.0, create_time=2023-06-07 15:52:40.0, project_id=1, level=1, parent_id=1, order_rule=1, machine_num=1, id=1, order_num=1}, {area_name=测试, update_time=2023-06-07 18:14:42.0, create_time=2023-06-07 18:14:42.0, project_id=2, level=1, parent_id=0, order_rule=1, machine_num=0, id=2, order_num=1}]
#AreaMapper接口
List<Map<String,Object>> getAreaListMap(@Param(value = “areaName”) String areaName);
#AreaMapper.xml <select id=“getAreaListMap” resultType=“java.util.Map”>
select * from vrv_area where area_name = #{areaName} </select> -
-
方式二
结果:{1={area_name=测试, update_time=2023-06-07 15:52:40.0, create_time=2023-06-07 15:52:40.0, project_id=1, level=1, parent_id=1, order_rule=1, machine_num=1, id=1, order_num=1}, 2={area_name=测试, update_time=2023-06-07 18:14:42.0, create_time=2023-06-07 18:14:42.0, project_id=2, level=1, parent_id=0, order_rule=1, machine_num=0, id=2, order_num=1}}
#AreaMapper接口 @MapKey("id") Map<String,Object> getAreaListMap2(@Param(value = "areaName") String areaName); #AreaMapper.xml <select id="getAreaListMap2" resultType="java.util.Map"> select * from vrv_area where area_name = #{areaName} </select>
3、高级结果映射
-
一对一查询
<association property=“imuSerialCode” javaType=“ImuSerialCode”>
imuSerialCode对应SystemDto 的属性imuSerialCode
javaType对应ImuSerialCode类型
#SystemDto实体类 @Data @ToString public class SystemDto extends System { private ImuSerialCode imuSerialCode; } #SystemMapper接口 SystemDto getSystemImu(Long id); #SystemMapper.xml <resultMap id="SystemImuResultMap" type="SystemDto"> <id column="id" jdbcType="BIGINT" property="id" /> <result column="create_time" jdbcType="TIMESTAMP" property="createTime" /> <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" /> <result column="system_name" jdbcType="VARCHAR" property="systemName" /> <result column="project_id" jdbcType="BIGINT" property="projectId" /> <result column="external_machine_num" jdbcType="INTEGER" property="externalMachineNum" /> <result column="inner_machine_num" jdbcType="INTEGER" property="innerMachineNum" /> <result column="inductance_multiple" jdbcType="DOUBLE" property="inductanceMultiple" /> <association property="imuSerialCode" javaType="ImuSerialCode"> <id column="id" jdbcType="BIGINT" property="id" /> <result column="create_time" jdbcType="TIMESTAMP" property="createTime" /> <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" /> <result column="create_account" jdbcType="VARCHAR" property="createAccount" /> <result column="imu_serial_code" jdbcType="VARCHAR" property="imuSerialCode" /> <result column="system_info_id" jdbcType="BIGINT" property="systemInfoId" /> <result column="state" jdbcType="INTEGER" property="state" /> <result column="imu_enable" jdbcType="VARCHAR" property="imuEnable" /> <result column="timestamp" jdbcType="BIGINT" property="timestamp" /> <result column="time_stamp" jdbcType="VARCHAR" property="timeStamp" /> <result column="type" jdbcType="INTEGER" property="type" /> <result column="is_charge" jdbcType="INTEGER" property="isCharge" /> <result column="version" jdbcType="VARCHAR" property="version" /> <result column="upgrade_time" jdbcType="TIMESTAMP" property="upgradeTime" /> <result column="is_auto_acquire" jdbcType="CHAR" property="isAutoAcquire" /> <result column="system_load" jdbcType="INTEGER" property="systemLoad" /> <result column="low_standby" jdbcType="INTEGER" property="lowStandby" /> </association> </resultMap> <select id="getSystemImu" resultMap="SystemImuResultMap" parameterType="long"> select sys.*,imu.* from vrv_system sys left join vrv_imu_serial_code imu on sys.id = imu.system_info_id where sys.id = #{id} </select>
也可以定义一个SystemDto类,包含System类和ImuSerialCode类所需查询的属性,用resultType的形式接收(略)
<select id="getSystemImu" resultType="SystemDto" parameterType="long">
select sys.*,imu.* from vrv_system sys left join vrv_imu_serial_code imu on sys.id = imu.system_info_id where sys.id = #{id}
</select>
-
一对多查询
如果Project 和 System 的主键都为id 则System的id要起一个别名
<collection property=“systems” ofType=“System”> <id column=“systemId” jdbcType=“BIGINT” property=“id” />
sys.id as systemId
#ProjectMapper接口 ProjectDto getProjectDto(Long projectId); #ProjectMapper.xml <resultMap id="ProjectDtoBaseResultMap" type="ProjectDto"> <id column="id" jdbcType="BIGINT" property="id" /> <result column="create_time" jdbcType="TIMESTAMP" property="createTime" /> <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" /> <result column="haier_contact" jdbcType="VARCHAR" property="haierContact" /> <result column="haier_mobile" jdbcType="VARCHAR" property="haierMobile" /> <result column="haier_position" jdbcType="VARCHAR" property="haierPosition" /> <result column="is_default" jdbcType="INTEGER" property="isDefault" /> <result column="join_time" jdbcType="TIMESTAMP" property="joinTime" /> <result column="latitude" jdbcType="VARCHAR" property="latitude" /> <result column="longitude" jdbcType="VARCHAR" property="longitude" /> <result column="project_name" jdbcType="VARCHAR" property="projectName" /> <result column="status" jdbcType="INTEGER" property="status" /> <result column="user_company" jdbcType="VARCHAR" property="userCompany" /> <result column="user_contact" jdbcType="VARCHAR" property="userContact" /> <result column="user_mobile" jdbcType="VARCHAR" property="userMobile" /> <result column="user_position" jdbcType="VARCHAR" property="userPosition" /> <result column="city_code" jdbcType="BIGINT" property="cityCode" /> <result column="country_code" jdbcType="BIGINT" property="countryCode" /> <result column="industry_id" jdbcType="BIGINT" property="industryId" /> <result column="province_code" jdbcType="BIGINT" property="provinceCode" /> <result column="region_code" jdbcType="BIGINT" property="regionCode" /> <result column="trade_id" jdbcType="BIGINT" property="tradeId" /> <result column="device_type" jdbcType="INTEGER" property="deviceType" /> <result column="address" jdbcType="VARCHAR" property="address" /> <result column="corporate_name" jdbcType="VARCHAR" property="corporateName" /> <result column="open_billing_service" jdbcType="INTEGER" property="openBillingService" /> <result column="county_code" jdbcType="BIGINT" property="countyCode" /> <result column="user_id" jdbcType="BIGINT" property="userId" /> <result column="low_standby" jdbcType="INTEGER" property="lowStandby" /> <result column="use_time" jdbcType="TIMESTAMP" property="useTime" /> <collection property="systems" ofType="System"> <id column="systemId" jdbcType="BIGINT" property="id" /> <result column="create_time" jdbcType="TIMESTAMP" property="createTime" /> <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" /> <result column="system_name" jdbcType="VARCHAR" property="systemName" /> <result column="project_id" jdbcType="BIGINT" property="projectId" /> <result column="external_machine_num" jdbcType="INTEGER" property="externalMachineNum" /> <result column="inner_machine_num" jdbcType="INTEGER" property="innerMachineNum" /> <result column="inductance_multiple" jdbcType="DOUBLE" property="inductanceMultiple" /> </collection> </resultMap> <select id="getProjectDto" resultMap="ProjectDtoBaseResultMap" parameterType="long"> select pro.*,sys.id systemId,sys.create_time, sys.update_time, sys.system_name, sys.project_id, sys.external_machine_num, sys.inner_machine_num, sys.inductance_multiple from vrv_project pro left join vrv_system sys on pro.id = sys.project_id where pro.id = #{id} </select>
三、动态sql
-
if(where)
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
-
if一般搭配where标签同时使用 如果不用where标签两个条件都不匹配则报错
-
用where标签 前面的AND 如果没用则会自动省略
#AreaMapper接口
List selectList(@Param(value = “areaName”) String areaName,@Param(value = “id”) Long id);#AreaMapper.xml <select id=“selectList” resultMap=“BaseResultMap”>
select id, create_time, update_time, area_name, machine_num, parent_id, project_id,
level, order_num, order_rule
from vrv_area <where> <if test=“areaName != ‘’ and areaName!=null”>
AND area_name = #{areaName} </if> <if test=“id != null”>
AND id = #{id} </if> </where> </select>
-
-
choose (when, otherwise)
when匹配成功则不匹配otherwise,when都匹配不成功则匹配otherwise
#AreaMapper接口
List selectListChooese(AreaParam areaParam);#AreaMapper.xml <select id="selectListChooese" resultMap="BaseResultMap" parameterType="AreaParam"> select id, create_time, update_time, area_name, machine_num, parent_id, project_id, level, order_num, order_rule from vrv_area where level = 1 <choose> <when test="projectId !=null"> AND project_id = #{projectId} </when> <when test="areaName !=null and areaName != ''"> AND area_name= #{areaName} </when> <otherwise> AND id = #{id} </otherwise> </choose> </select>
-
trim (set)
下面的代码 跟where标签功能类似
#AreaMapper接口
List selectListTrim(AreaParam areaParam);#AreaMapper.xml <select id="selectListTrim" resultMap="BaseResultMap" parameterType="AreaParam"> select id, create_time, update_time, area_name, machine_num, parent_id, project_id, level, order_num, order_rule from vrv_area <trim prefix="where" prefixOverrides="and "> <if test="areaName != '' and areaName!=null"> AND area_name = #{areaName} </if> <if test="id != null"> AND id = #{id} </if> </trim> </select>
-
prefix:
- 表示在trim包裹的SQL语句前面添加的指定内容。
-
suffix:
- 表示在trim包裹的SQL末尾添加指定内容
-
prefixOverrides:
- 表示去掉(覆盖)trim包裹的SQL的指定首部内容
-
suffixOverrides:
- 表示去掉(覆盖)trim包裹的SQL的指定尾部内容
set(<trim prefix=“set” suffixOverrides=“,”>与set功能类似)
- 表示去掉(覆盖)trim包裹的SQL的指定尾部内容
#AreaMapper接口
int updateSet(@Param(value = “updateTime”)Date updateDate,@Param(value = “areaName”)String areaName,@Param(value = “id”)Long id);#AreaMapper.xml <update id=“updateSet”>
update vrv_area <set> <if test=“updateTime!=null”>
update_time = #{updateTime,jdbcType=TIMESTAMP}, </if> <if test=“areaName!=null and areaName!=‘’”>
area_name = #{areaName,jdbcType=VARCHAR}, </if> </set> -
-
foreach
foreach 元素的属性主要有 item,index,open,separator,close,collection。各属性含义如下所示。
-
item 集合中元素迭代时的别名,该参数为必选。
-
index 在 list 和数组中,index 是元素的序号;在 map 中,index 是元素的 key。该参数可选。
-
open foreach 代码的开始符号,一般是 ”(“,和 close=“)” 合用。常用在 in(),values() 时。该参数可选。
-
separator 元素之间的分隔符,例如在 in() 的时候,separator=“,” 会自动在元素中间用 “,“ 隔开,避免手动输入逗号导致 SQL 错误,如 in(1, 2,) 这样。该参数可选。
-
close foreach 代码的关闭符号,一般是 ”)“,和 open=“(” 合用。常用在 in(),values()时。该参数可选。
-
collection 要被 foreach 标签循环解析的对象。
- 具名参数 java 方法中使用了 @Param 注解指定了参数名称,则 foreach 中的 collection 属性必须为参数名
- 匿名参数 当在 java 方法中没有通过 @Param 注解指定参数名时,列表类型默认参数名为 ”list“,数组类型默认参数名为 ”array“,Map 对象没有默认值。
-
#AreaMapper接口 List<Area> getAreaIn(List<Long> ids); #AreaMapper.xml <select id="getAreaIn" resultMap="BaseResultMap"> select <include refid="attr"></include> from vrv_area where id in <foreach item="item" collection="list" separator="," open="(" close=")" index=""> #{item, jdbcType=NUMERIC} </foreach> </select> #AreaMapper接口 int batchInsert(@Param("areas") List<Area> areas); #AreaMapper.xml <insert id="batchInsert"> insert into vrv_area (create_time, update_time, area_name, machine_num, parent_id, project_id, level, order_num, order_rule) values <foreach collection ="areas" item="entity" separator =","> (#{entity.createTime},#{entity.updateTime},#{entity.areaName}, #{entity.machineNum},#{entity.parentId},#{entity.projectId},#{entity.level},#{entity.orderNum},#{entity.orderRule}) </foreach> </insert>
-
四、mybatis的一级缓存和二级缓存
- 一级缓存时默认开启的
flushCache | 如果将其设置为 true,将导致每当此语句出现时刷新本地和第二级缓存 叫。默认值:对于选择语句。false |
---|---|
useCache | 如果将其设置为 true,将导致此语句的结果缓存在二级缓存中。默认值:对于选择语句。true |
#AreaTest
@Test
@Transactional(rollbackFor = Exception.class)
public void getAreaByNameAndIdCache(){
Area area = areaService.getAreaByNameAndId("测试",1L);
Area area1 = areaService.getAreaByNameAndId("测试",1L);
Area area2 = areaService.getAreaByNameAndId("测试",1L);
System.out.println(area);
}
#flushCache="true" 每次查询都刷新缓存
<select id="selectByAreaNameAndId" resultMap="BaseResultMap" flushCache="true">
select * from vrv_area where area_name = #{areaName} and id = #{id}
</select>
-
二级缓存开启(非特殊情况不开启)
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> 这种配置创建了一个 FIFO 缓存,该缓存每 60 秒刷新一次,存储对结果对象或列表的 512 个引用,并且返回的对象被视为只读,因此 修改它们可能会导致不同线程中的调用方之间发生冲突。 可用的逐出策略包括: * `LRU`– 最近最少使用:删除在 时间。 * `FIFO`– 先进先出:按对象进入缓存的顺序删除对象。 * `SOFT`– 软引用:根据垃圾回收器状态和规则删除对象 软引用。 * `WEAK`– 弱引用:根据垃圾回收器状态更积极地删除对象 和弱引用规则。 #缓存引用 <cache-ref namespace="com.someone.application.data.SomeMapper"/>
#AreaController
@GetMapping("/cache2")
public void cache2(){
AreaParam areaParam = new AreaParam();
areaParam.setId(1L);
areaParam.setAreaName("测试");
Area area = areaMapper.selectByAreaParam(areaParam);
System.out.println(area);
}
#AreaMapper
Area selectByAreaParam(AreaParam areaParam);
#AreaMapper.xml
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
<!-- useCache="true" 开启二级缓存 默认true useCache="false" 关闭此查询的二级缓存 配置flushCache="true" 二级缓存也会失效-->
<select id="selectByAreaParam" parameterType="AreaParam" resultType="Area" useCache="true">
select * from vrv_area where area_name = #{areaName} and id = #{id}
</select>