Mybatis系列
Mybatis快速入门
前言
本文是单纯的Mybatis项目,未与spring boot整合,所以配置上和我们平时在spring boot中配置mybatis不一样;本文主旨是介绍用法,原理会在后续的系列博文中介绍。
什么是Mybatis
Mybatis前身是iBatis,其源于“Internet”和“ibatis”的组合,本质是一种半自动的ORM(Object Relational Mapping,直接可以按字面意思理解这个架构:对象、关系、映射)框架,说它半自动,是因为除了POJO和映射关系之外,还需要编写SQL语句;
如何在项目中配置Mybatis
本文是单纯的Mybatis项目,未与spring boot整合的配置,下面是博主的一个项目结构:
1.加入mybatis的依赖
博主用的是Maven,这里就只介绍Maven中的依赖,依赖版本请根据自己的需要和实际版本迭代选择。
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.25</version>
</dependency>
2.添加Mybatis的配置文件
在与spring boot 整合中已经实现零配置了,不过单独的Myabtis项目还是需要自己配置滴。
<?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>
<properties resource="db.properties"/>
<settings>
<!--全局全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为true-->
<setting name="cacheEnabled" value="true"/>
<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置
fetchType属性来覆盖该项的开关状态。默认值为false -->
<setting name="lazyLoadingEnabled" value="false"/>
<!--当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载,默认值false-->
<setting name="aggressiveLazyLoading" value="false"/>
<!--是否允许单一语句返回多结果集,默认值为true -->
<setting name="multipleResultSetsEnabled" value="true"/>
<!--使用列标签代替列名,默认值为true -->
<setting name="useColumnLabel" value="true"/>
<!--允许 JDBC 支持自动生成主键,需要驱动兼容,默认值为false -->
<setting name="useGeneratedKeys" value="false"/>
<!--指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL
只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集
(无论是否嵌套),默认值为PARTIAL-->
<setting name="autoMappingBehavior" value="PARTIAL"/>
<!--指定发现自动映射目标未知列(或者未知属性类型)的行为。NONE: 不做任何反应;
ARNING: 输出提醒日志 ('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior'
的日志等级必须设置为 WARN);FAILING: 映射失败 (抛出 SqlSessionException),默认值为NONE -->
<setting name="autoMappingUnknownColumnBehavior" value="NONE"/>
<!--配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements);
BATCH 执行器将重用语句并执行批量更新。默认值为SIMPLE -->
<setting name="defaultExecutorType" value="SIMPLE"/>
<!--设置超时时间,它决定驱动等待数据库响应的秒数。参数为任意正整数,未设置默认值-->
<setting name="defaultStatementTimeout" value="25"/>
<!--为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。参数为任意正整数,
未设置默认值 -->
<setting name="defaultFetchSize" value="100"/>
<!--允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false,默认值未false -->
<setting name="safeRowBoundsEnabled" value="false"/>
<!--允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false。默认值为true -->
<setting name="safeResultHandlerEnabled" value="true"/>
<!--是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java
属性名 aColumn 的类似映射,默认值为false -->
<setting name="mapUnderscoreToCamelCase" value="false"/>
<!--MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。
默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语
句执行上,对相同 SqlSession 的不同调用将不会共享数据。-->
<setting name="localCacheScope" value="SESSION"/>
<!--当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情
况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。默认值为OTHER -->
<setting name="jdbcTypeForNull" value="OTHER"/>
<!--指定哪个对象的方法触发一次延迟加载。 -->
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
<!--指定动态 SQL 生成的默认语言。-->
<setting name="defaultScriptingLanguage" value="org.apache.ibatis.scripting.xmltags.XMLLanguageDriver"/>
<!--指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或
null 值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null 的。-->
<setting name="callSettersOnNulls" value="false"/>
<!--当返回行的所有列都是空时,MyBatis默认返回null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的
结果集 (i.e. collectioin and association)。(从3.4.2开始-->
<setting name="returnInstanceForEmptyRow" value="false"/>
<!--指定 MyBatis 增加到日志名称的前缀
<setting name="logPrefix" value="log"/>-->
<!--指定 MyBatis 所用日志的具体实现,未指定时将自动查找
SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING。-->
<setting name="logImpl" value="LOG4J"/>
<!--指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。CGLIB | JAVASSIST-->
<setting name="proxyFactory" value="JAVASSIST"/>
<!--指定VFS的实现
<setting name="vfsImpl" value="vfs"/>-->
<!--允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,并且加上-parameters选项。
(从3.4.1开始)-->
<setting name="useActualParamName" value="true"/>
<!--指定一个提供Configuration实例的类。 这个被返回的Configuration实例用来加载被反序列化对象的懒加载属性值。
这个类必须包含一个签名方法static Configuration getConfiguration(). (从 3.2.3 版本开始)
<setting name="configurationFactory" value="configClass"/>-->
</settings>
<typeAliases>
<!-- <typeAlias type="cn.enjoy.pojo.ConsultConfigArea" alias="Area"/>-->
<package name="cn.enjoy.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${mybatis.druid.driver-class-name}"/>
<property name="url" value="${mybatis.druid.url}"/>
<property name="username" value="${mybatis.druid.username}"/>
<property name="password" value="${mybatis.druid.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="xml/CommomMapper.xml"/>
<mapper resource="xml/TUser1Mapper.xml"/>
<!-- <package name="cn.enjoy.dao"/>-->
</mappers>
</configuration>
3.编写实体类、mapper接口以及mapper.xml
这个就不贴代码了,各位按照对应关系,创建即可。
使用说明
基本理论
在介绍使用方法前先介绍几个基本理论:
SqlSessionFactoryBuilder:读取配置信息创建SqlSessionFactory,建造者模式,方法级别生命周期;
SqlSessionFactory:创建Sqlsession,工厂单例模式,存在于程序的整个生命周期;
**SqlSession **:代表一次数据库连接,一般通过调用Mapper访问数据库,也可以直接发送SQL执行, ;线程不安全,要保证线程独享(方法级);
SQL Mapper:由一个Java接口和XML文件组成,包含了要执行的SQL语句和结果集映射规则。方法级别生命周期;
下面是一个基本流程:
使用
结合上面的流程图我们来介绍代码中使用(代码都比较简单我就不过多介绍了),基本上了解一下就可以了,在实际项目工程中基本上不需要这么配置:
public void test1() {
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
// System.out.println(sqlSession.selectList("cn.enjoy.dao.CommonMapper.queryAreaByAreaCode", new HashMap<>()));
CommonMapper mapper = sqlSession.getMapper(CommonMapper.class);
System.out.println(mapper.queryAreaByAreaCode(new HashMap()));
// sqlSession.commit();
}
xml文件
上面那些介绍诸位过一下就可以,基本上项目上处理引用依赖都不会这么用,主要是下面关于xml中的一些使用,基本上在我们平时工作学习上都用得上。
resultType 还是 resultMap
介绍这两个参数之前先介绍一个概念:
Mytais自动映射:MyBatis可以将sql执行结果的列名与JavaBean的属性字段进行自动映射。但是这种自动映射是有限制条件的:
- SQL列名和JavaBean的属性是一致的;
- 使用resultType,如用简写需要配置typeAliases (别名);
- 如果列名和JavaBean不一致,但列名符合单词下划线分割,Java是驼峰命名法,则mapUnderscoreToCamelCase可设置为true;
resultType:我们用这个参数就只需要指定接受返回结果的对象的全限定名(即完整的包名+类名,)或者是实体类的别名,如果是八大基本类型的话直接写类型名称也可以,但是要注意,如果是实体类的话返回的数据的字段要和负责接受的实体类的字段对应。(个人建议不要用resultType,除非返回结果是八大基本类型,因为如果数据库字段发生变化了,你一定要去改类的,然后使用该类的地方的代码可能都要改,这就会增加工作量)。
<select id="queryCardIdInfo" resultType="ConsultIdCardInfo">
select * from consult_idcardinfo
</select>
**resultMap**:使用resultMap的话我们需要在xml文件中先配置数据库字段与实体类字段的映射关系,我们还要指定对应的实体类是谁(可以使用实体类的别名或者全限定名),使用resultMap就能很好的解决我上面说的resultType的痛点。
<resultMap id="CountResultMap" type="cn.enjoy.pojo.ConsultRecordCount">
<result column="PSPTID" property="psptId" jdbcType="VARCHAR"/>
<result column="ISPRODUCE" property="isproduce"/>
<result column="UNPRODUCE" property="unproduce"/>
</resultMap>
传参
代码向sql传参数有三种方式:
使用map传参:
- 在mapper接口文件中使用map,在server调用的时候直接将参数封装到map中,通过map传递给sql
List<ConsultRecord> queryConsultRecords(Map param);
- 在xml文件中:
<select id="queryConsultRecords" parameterType="java.util.Map" resultMap="RECORDResultMap"> select ID,PSPTID,date_format(ACTIVETIME,'%Y-%m-%d') ACTIVETIME,date_format(ACTIVETIME,'%Y-%m-%d %T') ACTIVETIME1,AUTOGRAPH,ISPASS,DOCAUTOGRAPH,FINGERPRINT,PRINT_FLAG,REMARK from consult_record where PSPTID = #{psptId,jdbcType=VARCHAR} and hand_state = 'C' order by ACTIVETIME1 desc </select>
使用实体类传参:
- 在mapper接口中:
List<Map> queryUserByPsptIdObj(ConsultIdCardInfo info);
- 在xml中:
<select id="queryUserByPsptIdObj" parameterType="ConsultIdCardInfo" resultType="java.util.Map"> select *,date_format(birthday,'%Y-%m-%d') birthday1,date_format(activeTime,'%Y-%m-%d') activeTime1 from consult_idcardinfo where psptId = #{psptId,jdbcType=VARCHAR} </select>
使用@Param注解:
- 在mapper接口中:
List<Map> queryUserByPsptIdParam(@Param("psptId") String psptId);
- 在xml中
<select id="queryUserByPsptIdParam" resultType="java.util.Map"> select *,date_format(birthday,'%Y-%m-%d') birthday1,date_format(activeTime,'%Y-%m-%d') activeTime1 from consult_idcardinfo where psptId = #{psptId,jdbcType=VARCHAR} </select>
xml中接受参数 #{} 还是${}
#{}:预编译将传入的数据都当成一个字符串,会对自动传入的数据加一个单引号,能够很大程度防止sql注入;
${}:传入的数据直接显示生成在sql中,无法防止sql注入;
表名、选取的列是动态的,order by和in操作, 可以考虑使用**${}**,但是这样使用的话,我们就需要在代码层面去额外处理过滤,防止sql注入了。
Mybatis批量操作
通过foreach动态拼装SQL语句
这种方式在mapper接口传入list后,只需要在xml中使用forech对传入的参数进行动态拼接即可,这种方式进行批量操作IO只有一次,但是内存消耗回答一下,执行速率快。
<delete id="deleteArea" parameterType="cn.enjoy.pojo.ConsultConfigArea">
delete from consult_configarea where areaCode in
<foreach item="item" index="index" collection="areaCodes" open="(" separator="," close=")">
#{item}
</foreach>
</delete>
使用BATCH类型的excutor
使用batch就需要我们手动拼接,同时需要将Mybtais自动提交关闭,这种方式需要对数据库进行多次IO操作,执行速度较慢。
public void batchInsert() {
SqlSession sqlSession = getBatchSqlSession();
CommonMapper mapper = sqlSession.getMapper(CommonMapper.class);
Long t1 = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
ConsultContract consultContract = new ConsultContract();
consultContract.setActiveTime("2021-3-10");
consultContract.setContractCode("ER");
consultContract.setPsptId("456979432");
consultContract.setState(1);
System.out.println(mapper.saveContractSelectKey(consultContract));
}
sqlSession.commit();
sqlSession.close();
System.out.println(System.currentTimeMillis() - t1);
}