玄子Share - mybatis-3.5.13 学习笔记
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-edDRdx4N-1684580838688)(./assets/68747470733a2f2f6d7962617469732e6f72672f696d616765732f6d7962617469732d6c6f676f2e706e67.png)]
mybatis 官网
官网 https://mybatis.org/mybatis-3/zh/index.html
下载地址
下载地址 https://github.com/mybatis/mybatis-3
Maven 地址
Maven https://mvnrepository.com/artifact/org.mybatis/mybatis
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.13</version>
</dependency>
XML 构建失败
在 Maven 中指定文件地址
<build><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include><include>**/*.properties</include></includes></resource><resource><directory>src/main/resources</directory><includes><include>**/*.xml</include><include>**/*.properties</include></includes></resource></resources>
</build>
清理 Maven 构建项目
mvn clean install
mybatis-config.xml
环境(environment)
写死
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url"value="jdbc:mysql://localhost:3306/hospital?TRUE&useUnicode=TRUE&characterEncoding=UTF-8&serverTimezone=GMT%2B8"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><mappers><mapper resource="com/xuanzishare/dao/PatientMapper.xml"/></mappers>
</configuration>
- $ 使用
&
转义
动态插入
mybatis-config.xml
这些属性可以在外部进行配置,并可以进行动态替换。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><properties resource="database.properties"/><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment></environments><mappers><mapper resource="com/xuanzishare/dao/PatientMapper.xml"/></mappers>
</configuration>
database.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/hospital?useUnicode=TRUE&characterEncoding=UTF-8&serverTimezone=GMT-8
username=root
password=root
重构属性
你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
<properties resource="org/mybatis/example/config.properties"><property name="username" value="root"/><property name="password" value="root"/>
</properties>
类型别名(typeAliases)
类名限定
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
<typeAliases><typeAlias type="com.xuanzishare.entity.Patient" alias="Patient"/><typeAlias type="com.xuanzishare.dao.PatientMapper.xml" alias="PatientMapper"/>
</typeAliases>
包名
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
<typeAliases><package name="com.xuanzishare.entity/"/>
</typeAliases>
每一个在包
com.xuanzishare.entity
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
注解
有注解,别名为其注解值
@Alias("patient")
public class Patient {private int patientId;private String password;private String birthDate;private String gender;
}
如果 IDEA 提示无法解析别名,但代码正常运行,可尝试重启IDEA刷新缓存
MybatisUtil.java
在 dao 包内
package com.xuanzishare.dao;import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;
import java.io.InputStream;public class MybatisUtil {private static SqlSessionFactory sqlSessionFactory;static {String resource = "mybatis-config.xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(resource);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (IOException e) {throw new RuntimeException(e);}}public static SqlSession getSqlSession() {return sqlSessionFactory.openSession();}
}
自动提交
public static SqlSession getSqlSession() {return sqlSessionFactory.openSession(true);}
DaoMapper.xml
使用标签指定
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xuanzishare.dao.PatientMapper"><insert id="insertPatientInfo" parameterType="com.xuanzishare.entity.Patient">insert into patient (password, birthDate, gender, patientName, phoneNum, email, identityNum, address) values(#{password},#{birthDate},#{gender},#{patientName},#{phoneNum},#{email},#{identityNum},#{address});</insert><update id="updatePatientInfoByPatientId" parameterType="com.xuanzishare.entity.Patient">update patient set password=#{password} where patientID= #{patientId}</update><delete id="deletePatientByPatientId" parameterType="int">delete from patient where patientID= #{patientId};</delete><select id="selectPatient" resultType="com.xuanzishare.entity.Patient" parameterType="int">select * from patient where patientID=#{id};</select>
</mapper>
- namespace:dao 层类名
- id:方法名
- parameterType:传参类型
- resultType:返回值类型
标签注入
写 sql 语句不提示,检查 IDEA 是否连接数据库并指定架构,如果还不行就点左边的小灯泡自行配置语言注入为 MySQL
万能 Map
使用 key 自定义字段
Map<String, Object> patientMap = new HashMap<>();
patientMap.put("id", 1);
patientMap.put("pwd", 12341);
patientMapper.updatePatientInfoByPatientId2(patientMap);
<update id="updatePatientInfoByPatientId2" parameterType="map">update patientset password=#{pwd}where patientID = #{id}
</update>
结果集映射
<select id="selectPatientInfo" resultMap="PatientMap">select *from patientwhere gender = #{gender};
</select><resultMap id="PatientMap" type="Patient"><id property="patientId" column="patientID"/><result property="password" column="password"/><result property="gender" column="gender"/>
</resultMap>
- resultMap:结果集
- resultMap 里的 id 就是 resultMap 的值 可以随便起名字
- typ:返回值类型
- property:属性名
- column:数据库字段名
Main.java
获取对象 执行 sql
package com.xuanzishare;import com.xuanzishare.dao.MybatisUtil;
import com.xuanzishare.dao.PatientMapper;
import com.xuanzishare.entity.Patient;
import org.apache.ibatis.session.SqlSession;public class Main {public static void main(String[] args) {SqlSession sqlSession = MybatisUtil.getSqlSession();// 获取 sqlSession 对象PatientMapper patientMapper = sqlSession.getMapper(PatientMapper.class);// 获取 patientMapper 对象for (Patient patient : patientMapper.selectPatient(1)) {System.out.println(patient);}patientMapper.updatePatientInfoByPatientId(new Patient(4, "123555", "2022-12-01", "1", "1", "1", "1", "1daga", "1"));sqlSession.commit();// 提交事务sqlSession.close();// 关闭流}
}
List<Patient> objects = sqlSession.selectList("com.xuanzishare.dao.PatientMapper.selectPatient");
//获取 patientMapper 对象 方法
for (Patient object : objects) {System.out.println(object);
}
日志 log4j 2
Maven 地址
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.20.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.20.0</version>
</dependency>
mybatis-config.xml
<settings><setting name="logImpl" value="LOG4J2"/>
</settings>
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J(3.5.9 起废弃) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
分页
select * from patientlimit startIndex,pageSize;
# 起始下标 页面大小
<select id="selectPatientInfoLimit" resultType="Patient" parameterType="map">select *from patientlimit #{startIndex},#{pageSize};
</select>
PatientMapper patientMapper = sqlSession.getMapper(PatientMapper.class);Map<String, Object> patientLimitMap = new HashMap<>();patientLimitMap.put("startIndex", 0);patientLimitMap.put("pageSize", 10);List<Patient> patientList = patientMapper.selectPatientInfoLimit(patientLimitMap);for (Patient patient : patientList) {System.out.println(patient);}
注解开发
@Select( )
package com.xuanzishare.dao;import com.xuanzishare.entity.Checkitem;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;import java.util.List;public interface CheckitemMapper {@Select("select * from Checkitem")List<Checkitem> selectCheckItemInfo();@Select("select * from Checkitem where checkitemId =#{checkItemID} and checkitemName =#{CheckitemName}")List<Checkitem> selectCheckByIdAndCheckitemName(@Param("checkItemID") int id, @Param("CheckitemName") String name);@Insert("insert into Checkitem (CheckitemName,checkItemCost) values (#{checkItemName},#{checkItemCost})")int insertCheckItem(Checkitem checkitem);
}
- @Select( ):注解标签
- @Param( ):参数类型
- 基本数据类型:一个可以不写,多个写,最好都写
- 对象引用类型:不用写
只能执行简单的增删改,无法写业务
Lombok
常用
@Data
@AllArgsConstructor
@NoArgsConstructor
- @Data:基础POJO
- @AllArgsConstructor:有参构造
- @NoArgsConstructor:无参构造
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@StandardException
@val
@var
experimental @var
@UtilityClass
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.26</version>
</dependency>
多对一 与 一对多
package com.xuanzishare.dao;import com.xuanzishare.entity.Checkitem;
import com.xuanzishare.entity.Department;
import com.xuanzishare.entity.DepartmentCheckitem;import java.util.List;public interface DepartmentCheckitemMapper {List<DepartmentCheckitem> selectDepartmentCheckitem();List<Department> selectDepartment();List<Checkitem> selectCheckitem();// 多对一 多个检查项目对应一个科室List<DepartmentCheckitem> selectDepartmentCheckitem2();List<Department> selectDepartment2();List<Checkitem> selectCheckitem2();
// 一对多 一个科室对应多个检查项目}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xuanzishare.dao.DepartmentCheckitemMapper"><!-- 多对一 多个检查项目对应一个科室 --><select id="selectDepartmentCheckitem" resultMap="dcMap">select *from department_checkiteminner join department on department_checkitem.depID = department.depIDinner join checkitem on department_checkitem.checkItemID = checkitem.checkItemID;</select><select id="selectCheckitem" resultType="Checkitem">select *from Checkitemwhere checkItemID = #{checkItemID};</select><select id="selectDepartment" resultType="Department">select *from departmentwhere depID = #{depID};</select><!--第一种,多对一,按照查询嵌套处理--><!-- <resultMap id="dcMap" type="DepartmentCheckitem">--><!-- <id property="id" column="id"/>--><!-- <association property="checkitem" column="checkItemID" javaType="Checkitem" select="selectCheckitem"/>--><!-- <association property="department" column="depID" javaType="Department" select="selectDepartment"/>--><!-- </resultMap>--><!--第二种,多对一 按照结果嵌套查询--><resultMap id="dcMap" type="DepartmentCheckitem"><id property="id" column="id"/><association property="checkitem" javaType="Checkitem"><id property="checkItemId" column="checkItemID"/><result property="checkItemName" column="checkItemName"/><result property="checkItemCost" column="checkItemCost"/></association><association property="department" javaType="Department"><id property="depId" column="depID"/><result property="depName" column="depName"/></association></resultMap><!-- =================================================== --><!-- =================================================== --><!-- 一对一 一个科室对应多个检查项目 --><select id="selectDepartmentCheckitem2" resultMap="dcMap2">SELECT *FROM department_checkitemLEFT JOIN department ON department_checkitem.depID = department.depIDLEFT JOIN checkitem ON department_checkitem.checkItemID = checkitem.checkItemID;</select><select id="selectDepartment2" resultType="Department">select *from Checkitemwhere checkItemID = #{checkItemID};</select><select id="selectCheckitem2" resultType="Checkitem">select *from departmentwhere depID = #{depID};</select><!-- 第一种,一对多,按照查询嵌套处理--><resultMap id="dcMap2" type="DepartmentCheckitem"><id property="id" column="id"/><collection property="checkitem" column="checkItemID" javaType="Checkitem" select="selectCheckitem"/><collection property="department" column="depID" javaType="Department" select="selectDepartment"/></resultMap><!--第二种,一对多 按照结果嵌套查询--><!-- <resultMap id="dcMap2" type="DepartmentCheckitem">--><!-- <id property="id" column="id"/>--><!-- <collection property="checkitem" ofType="Checkitem">--><!-- <id property="checkItemId" column="checkItemID"/>--><!-- <result property="checkItemName" column="checkItemName"/>--><!-- <result property="checkItemCost" column="checkItemCost"/>--><!-- </collection>--><!-- <collection property="department" ofType="Department">--><!-- <id property="depId" column="depID"/>--><!-- <result property="depName" column="depName"/>--><!-- </collection>--><!-- </resultMap>-->
</mapper>
多对一
- association:关联
- property:字段名
- column:数据库名称
- javaType:返回值类型
- select:子查询来源
一对多
- collection:集合
- property:字段名
- column:数据库名称
- ofType:返回值类型(集合泛型)
- select:子查询来源
动态 Sql
根据不同的条件,执行不同的 sql 类似于 java 的方法重载
if
// 动态 sql Map 限定传参 if 判断
List<Patient> selecPatientInfo(Map<String, Object> patientMap);
// Main 执行 sqlSqlSession sqlSession = MybatisUtil.getSqlSession();PatientMapper patientMapper = sqlSession.getMapper(PatientMapper.class);Map<String, Object> patientMap = new HashMap<>();patientMap.put("password", "312314");patientMap.put("gender", "男");patientMap.put("identityNum", "3223123");List<Patient> patientList = patientMapper.updatePatientInfo(patientMap);for (Patient patient : patientList) {System.out.println(patient);}
<select id="selecPatientInfo" resultType="Patient" parameterType="map">select * from patient where 1=1<if test="password != null">and password=#{password}</if><if test="gender != null">and gender=#{gender}</if>
</select>
- where 1=1 保证执行 查询
- test:判断调教成立执行 sql
choose (when, otherwise)
<select id="selecPatientInfo" resultType="Patient" parameterType="map">select * from patient<where><choose><when test="password != null">and password=#{password}</when><when test="gender != null">and gender=#{gender}</when><otherwise>identityNum=#{identityNum}</otherwise></choose></where>
</select>
- choose:类似于 java 中的 switch 关键字
- when:类似 java 中的 case 关键字
- otherwise:类似于 java 中的 default 关键字
- 如果 when 均不成立 才执行 otherwise
- when 自带 brea 机制,一个成立就直接结束后面的判断
trim (where, set)
<select id="selecPatientInfo" resultType="Patient" parameterType="map">select * from patient<where><if test="password != null">and password=#{password}</if><if test="gender != null">and gender=#{gender}</if></where>
</select>
- where:只有在有参数的时候才会执行
- 若子句的开头为
AND
或OR
,where
会自动去除(保险最好都写上)
// 动态 sql Map 限定传参 set 判断
int updatePatientInfo(Map<String, Object> patientMap);
// Main 执行 sqlSqlSession sqlSession = MybatisUtil.getSqlSession();PatientMapper patientMapper = sqlSession.getMapper(PatientMapper.class);Map<String, Object> patientMap = new HashMap<>();patientMap.put("password", "312314");patientMap.put("gender", "男");patientMap.put("identityNum", "3223123");int patientList = patientMapper.updatePatientInfo(patientMap);System.out.println(patientList + ":行受影响");
<update id="updatePatientInfo" parameterType="map">update patient<set><if test="password != null">password=#{password},</if><if test="gender != null">gender=#{gender},</if></set><where>identityNum=#{identityNum}</where>
</update>
- set:只有在有参数的时候才会执行
- 会自动删掉 sql 结尾额外的逗号(保险最好都写上)
<trim prefix="WHERE" prefixOverrides="AND |OR ">...
</trim><trim prefix="SET" suffixOverrides=",">...
</trim>
- trim:定制条件
- prefix:设置前缀
prefixOverrides
忽略通过|
分隔的内容(AND |OR
中的空格是必要的)- 移除所有
prefixOverrides
属性中指定的内容,并且插入prefix
属性中指定的内容
foreach
select * from patient where (patientID=1 and patientID=2 and patientID=3)
-- 这里也可以用 where patientID IN(1,2,3) 只是为了练习拼接 sql
// 动态 sql foreach 便利
List<Patient> selecPatientInfoForeach(Map<String, Object> patientMap);
// Main 执行 sqlSqlSession sqlSession = MybatisUtil.getSqlSession();PatientMapper patientMapper = sqlSession.getMapper(PatientMapper.class);Map<String, Object> patientMap = new HashMap<>();ArrayList<Integer> patientIdList = new ArrayList<>();patientIdList.add(1);patientIdList.add(2);patientIdList.add(5);patientMap.put("ids", patientIdList);List<Patient> patientList = patientMapper.selecPatientInfoForeach(patientMap);for (Patient patient : patientList) {System.out.println(patient);}
<select id="selecPatientInfoForeach" resultType="Patient" parameterType="map">select *from patient<where><foreach item="patientID" collection="ids" open="and (" separator="or" close=")">patientID=#{patientID}</foreach></where>
</select>
- foreach:遍历
- collection:集合名称,对应 map 的 key
- item:集合的每一项,对应 list 的每一项
- open:sql 以什么开头
- close:sql 以什么结尾
- separator: sql 以什么分隔
- 里面写拼接的 sql
在写这类复杂的拼接 可以先在数据库中写好 正确的完整 sql 在来替换拆分
sql 片段
在使用 sql 的时候 会有一些重复性的 判断,例如上面的案例,都判断了title
与gender
类似于这样的 sql 就可以抽离成 sql 片段,以复用
<update id="updatePatientInfo" parameterType="map">update patient<set><include refid="if-password-gender"/></set><where>identityNum=#{identityNum}</where>
</update><sql id="if-password-gender"><if test="password != null">password=#{password},</if><if test="gender != null">gender=#{gender},</if>
</sql>
- sql:抽离出来的 sql 片段
- id:片段名称(见名知意)
- include:在需要使用片段的地方,导入片段
- refid:sql 片段名称
- sql 片段不要包含 where 语句
- 尽量提取简单的 sql 片段以提高,复用性
script
要在带注解的映射器接口类中使用动态 SQL,可以使用script
元素。
@Update({"<script>","update Author"," <set>"," <if test='username != null'>username=#{username},</if>"," <if test='password != null'>password=#{password},</if>"," <if test='email != null'>email=#{email},</if>"," <if test='bio != null'>bio=#{bio}</if>"," </set>","where id=#{id}","</script>"})void updateAuthorValues(Author author);
bind
bind
元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。
<select id="selectBlogsLike" resultType="Blog"><bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />SELECT * FROM BLOGWHERE title LIKE #{pattern}
</select>
缓存
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
基本上就是这样。这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
提示 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。
这些属性可以通过 cache 元素的属性来修改。比如:
<cacheeviction="FIFO"flushInterval="60000"size="512"readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:
LRU
– 最近最少使用:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU。
flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
提示 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。
使用自定义缓存
除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。
Redis
玄子Share - mybatis-3.5.13 学习笔记 5.20