共计 16927 个字符,预计需要花费 43 分钟才能阅读完成。
Mybatis 映射器
映射器是 MyBatis 最弱小的⼯具,也是咱们应用 MyBatis 时⽤得最多的工具,因而熟
练把握它⼗分必要。MyBatis 是针对映射器结构的 SQL 构建的轻量级框架,并且通过配置
生成对应的 JavaBean 返回给调用者,⽽这些配置次要便是映射器,在 MyBatis 中你能够根
据状况定义动静 SQL 来满足不同场景的须要,它比其余框架灵便得多。MyBatis 还反对⾃动绑定 JavaBean,
咱们只有让 SQL 返回的字段名和 JavaBean 的属性名保持一致(或者采⽤驼峰式命名),便能够省掉这些繁琐的映射配置
目录:
[toc]
咱们先再回顾下映射器的次要元素
映射器的次要元素
映射器是由 Java 接口和 XML 文件(或注解)独特组成的,Java 接口次要定义调用者接口,XML 文件是配置映射器的外围文件,包含以下元素:
- select 查问语句,能够自定义参数,返回后果集;
- insert 插入语句,返回一个整数,示意插入的条数;
- update 更新语句,返回一个整数,示意更新的条数;
- delete 删除语句,返回一个整数,示意删除的条数;
- sql 容许定义一部分 SQL,而后再各个中央援用;
- resultMap 用来形容从数据库后果集中来加载对象,还能够配置关联关系, 提供映射规定;
-
cache 给定命名空间的缓存配置
Select 元素
select 元素帮忙咱们
从数据库中读出数据,组装数据给业务人员。执行 select 语句前,咱们须要定义参数,它
能够是⼀个简略的参数类型,例如 int, float , String, 也能够是⼀个简单的参数类型
例如
JavaBean、Map 等,这些都是 MyBatis 承受的参数类型。
执⾏ SQL 后,MyBatis 也提供了
强⼤的映射规定,主动映射来帮忙咱们把返回的后果集绑定到 JavaBean 中。
select
元素的配置泛滥, 上面简略阐明下:
- id: id 和 Mapper 的命名空间组成惟一值, 提供给 Mybatis 调用, 如果不惟一将会报错
- paramterType:传入的参数类型,能够是根本类型、map、自定义的 java bean;
- resultType:返回的后果类型,能够是根本类型、自定义的 java bean;
- resultMap:它是最简单的元素,能够配置映射规定、级联、typeHandler 等,与 ResultType 不能同时存在;
- flushCache:在调用 SQL 后,是否要求清空之前查问的本地缓存和二级缓存,次要用于更新缓存,默认为 false;
- useCache:启动二级缓存的开关,默认只会启动一级缓存;
- timeout:设置超时参数,等超时的时候将抛出异样,单位为秒;
- fetchSize:获取记录的总条数设定;
select 实例
需要: 查问名称等于 JAVA 宝典
的用户
在 UserDao 中定义接口办法:
User findIdByName(String name);
定义 UserMapper.xml
<select id="findIdByName" parameterType="string" resultType="User"> | |
select | |
u.* | |
from t_user u | |
where u.name=#{name} | |
</select> |
对操作步骤进行演绎概括:
- Id 标出了了这条 SQL
- parameterType 定义参数类型
- resuitType 定义返回值类型
下面的例子只是传入单个参数, 多个参数能够应用 Map,JavaBean, 应用注解形式, 等等, 上面咱们会独自介绍.
insert 元素
insert 属性和 select 大部分都雷同,说下 3 个不同的属性:
- keyProperty:指定哪个列是主键,如果是联结主键能够用逗号隔开;
- keyColumn:指定第几列是主键,不能和 keyProperty 共用;
- useGeneratedKeys:是否应用主动增长,默认为 false;当 useGeneratedKeys 设为 true 时,在插入的时候,会回填 Java Bean 的 id 值,通过返回的对象可获取主键值。
如果想依据一些非凡关系设置主键的值,能够在 insert 标签内应用 selectKey 标签
<insert id="insertRole" useGeneratedKeys="true" keyProperty="id" > | |
<selectKey keyProperty="id" resultType="int" order="before"> | |
select if(max(id) is null,1,max(id)+2) as newId from t_role | |
</selectKey> | |
</insert> |
update 和 delete 就不独自过多介绍了
sql 元素
sql 元素的意义,在于咱们能够定义⼀串串 SQL 语句的组成部分,其余的语句能够通过引⽤来使⽤它。
例如,你有一条 SQL 须要 select ⼏⼗个字段映射到 JavaBean 中去,我的第二
条 SQL 也是这⼏⼗个字段映射到 JavaBean 中去,显然这些字段写两遍不太适合。那么咱们
就⽤ sql 元素来实现
定义:
<sql id="columns"> | |
id, | |
name, | |
remark, | |
tid | |
</sql> |
应用:
<select id="getOne" resultType="com.liangtengyu.entity.Ttest"> | |
select <include refid="columns"/> from t_test where id = #{id} | |
</select> |
resultMap 元素
resultMap 是 MyBatis 外面最简单的元素,它的作用是定义映射规定、级联的更新、定制类型转换器等。
由以下元素形成:
<resultMap> | |
<constructor> <!-- 配置构造方法 --> | |
<idArg/> | |
<arg/> | |
</constructor> | |
<id/> <!-- 指明哪一列是主键 --> | |
<result/> <!-- 配置映射规定 --> | |
<association/> <!-- 一对一 --> | |
<collection/> <!-- 一对多 --> | |
<discriminator> <!-- 鉴别器级联 --> | |
<case/> | |
</discriminator> | |
</resultMap> |
有的实体不存在没有参数的构造方法,须要应用 constructor 配置有参数的构造方法:
<resultMap id="role" type="com.liangtengyu.entity.Role"> | |
<constructor> | |
<idArg column="id" javaType="int"/> | |
<arg column="role_name" javaType="string"/> | |
</constructor> | |
</resultMap> |
id 指明主键列,result 配置数据库字段和 POJO 属性的映射规定:
<resultMap id=”role” type=”com.liangtengyu.entity.Role”>
<id property="id" column="id" /> | |
<result property="roleName" column="role_name" /> | |
<result property="note" column="note" /> |
</resultMap>
association、collection 用于配置级联关系的,别离为一对一和一对多,理论中,多对多关系的利用不多,因为比较复杂,会用一对多的关系把它合成为双向关系。
discriminator 用于这样一种场景:比方咱们去体检,男和女的体检我的项目不同,如果让男生去查看妇科我的项目,是不合理的,通过 discriminator 能够依据性别,返回不同的对象。
级联关系的配置比拟多,就不在此演示了,可查看文档进行理解。
cache 元素
在没有显示配置缓存时,只开启一级缓存,一级缓存是绝对于同一个 SqlSession 而言的,在参数和 SQL 齐全一样的状况下,应用同一个 SqlSession 对象调用同一个 Mapper 的办法,只会执行一次 SQL。如果是不同的 SqlSession 对象,因为不同 SqlSession 是互相隔离的,即应用雷同的 Mapper、参数和办法,还是会再次发送 SQL 到数据库去执行。
二级缓存是 SqlSessionFactory 层面上的,须要进行显示配置
这样很多设置是默认的,有如下属性能够配置:
- eviction:代表缓存回收策略,可选值有 LRU 起码应用、FIFO 先进先出、SOFT 软援用,WEAK 弱援用;
- flushInterval:刷新间隔时间,单位为毫秒,如果不配置,当 SQL 被执行时才会刷新缓存;
- size:援用数目,代表缓存最多能够存储多少对象,不宜设置过大,设置过大会导致内存溢出;
- readOnly:只读,意味着缓存数据只能读取不能批改;
在大型服务器上,可能会应用专用的缓存服务器,比方 Redis 缓存,能够通过实现 org.apache.ibatis.cache.Cache 接口很不便的实现:
public interface Cache {String getId(); // 缓存编号 | |
void putObject(Object var1, Object var2); // 保留对象 | |
Object getObject(Object var1); // 获取对象 | |
Object removeObject(Object var1); // 移除对象 | |
void clear(); // 清空缓存 | |
int getSize(); // 获取缓存对象大小 | |
ReadWriteLock getReadWriteLock(); // 获取缓存的读写锁} |
映射器的外部组成
一般而言,一个映射器是由 3 个局部
组成:
关上 Mybatis 源码, 在 mapping 包中能够找到他们
MappedStatement
,它保留映射器的一个节点(select|insert|delete|update)并且包含许多咱们配置的 sql,sql 的 id、缓存信息,resultMap,parameterType、resultType、languageDriver 等重要的配置内容。
SqlSource
,它是提供 BoundSql 对象的中央,它是 MappedStatement 的一个属性。它的次要作用是依据参数和其余规定组装 sql。BoundSql
,它是建设 SQL 和参数的中央,他有 3 个罕用的属性:SQL
、parameterObject
、parameterMappings
这 3 个等会介绍.
idea 生成的依赖图
MappedStatement 创立过程
首先咱们介绍一下 MappedStatement
在 SqlSessionFactoryBuilder.build()办法, 它会帮咱们创立一个 SqlSessionFactory, 它的 build 办法:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { | |
try {XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); | |
return build(parser.parse());// 此处应用解析 XML 返回的数据构建 SqlSessionFactory 跟进去.. | |
} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e); | |
} finally {ErrorContext.instance().reset(); | |
try {inputStream.close(); | |
} catch (IOException e) {// Intentionally ignore. Prefer previous error.} | |
} | |
} |
进入到 parser.parse()办法看看:
public Configuration parse() {if (parsed) { // 如果一个 xml 文件解析两次 就会报错 否则才去解析 | |
throw new BuilderException("Each XMLConfigBuilder can only be used once."); | |
} | |
parsed = true; | |
parseConfiguration(parser.evalNode("/configuration"));// 解析节点 configuration, 跟进去... | |
return configuration; | |
} |
跟进代码 parseConfiguration(parser.evalNode(“/configuration”));
private void parseConfiguration(XNode root) { | |
try {propertiesElement(root.evalNode("properties")); | |
Properties settings = settingsAsProperties(root.evalNode("settings")); | |
loadCustomVfs(settings); | |
loadCustomLogImpl(settings); | |
typeAliasesElement(root.evalNode("typeAliases")); | |
pluginElement(root.evalNode("plugins")); | |
objectFactoryElement(root.evalNode("objectFactory")); | |
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); | |
reflectorFactoryElement(root.evalNode("reflectorFactory")); | |
settingsElement(settings); | |
environmentsElement(root.evalNode("environments")); | |
databaseIdProviderElement(root.evalNode("databaseIdProvider")); | |
typeHandlerElement(root.evalNode("typeHandlers")); | |
mapperElement(root.evalNode("mappers"));// 解析了很多, 然而明天咱们的配角是这个.. 跟进去. | |
} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause:" + e, e); | |
} | |
} |
private void mapperElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {if ("package".equals(child.getName())) {String mapperPackage = child.getStringAttribute("name"); | |
configuration.addMappers(mapperPackage); | |
} else {String resource = child.getStringAttribute("resource");// 获取三种形式指定的门路 进行判断 通知 MyBatis 到哪里去找映射文件 | |
String url = child.getStringAttribute("url"); | |
String mapperClass = child.getStringAttribute("class"); | |
if (resource != null && url == null && mapperClass == null) {ErrorContext.instance().resource(resource); | |
InputStream inputStream = Resources.getResourceAsStream(resource); | |
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); | |
mapperParser.parse();// 调用 mapperParser.parse(); 解析 xml 跟进去... | |
} else if (resource == null && url != null && mapperClass == null) {ErrorContext.instance().resource(url); | |
InputStream inputStream = Resources.getUrlAsStream(url); | |
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); | |
mapperParser.parse();// 调用 mapperParser.parse(); 解析 xml | |
} else if (resource == null && url == null && mapperClass != null) {Class<?> mapperInterface = Resources.classForName(mapperClass); | |
configuration.addMapper(mapperInterface); | |
} else {throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); | |
} | |
} | |
} | |
} | |
} |
能够看到在解析 mapper 标签
public void parse() {if (!configuration.isResourceLoaded(resource)) {configurationElement(parser.evalNode("/mapper"));// 解析 xml 的 mapper 标签, 并返回调用 configurationElement()办法 | |
configuration.addLoadedResource(resource); | |
bindMapperForNamespace();} | |
parsePendingResultMaps();// 短少资源的再尝试一下. 具体逻辑咱们不深刻走这里了, 有趣味的能够看看源码. | |
parsePendingCacheRefs(); | |
parsePendingStatements();} |
configurationElement()办法
private void configurationElement(XNode context) { | |
try {String namespace = context.getStringAttribute("namespace"); | |
if (namespace == null || namespace.isEmpty()) {throw new BuilderException("Mapper's namespace cannot be empty"); | |
} | |
builderAssistant.setCurrentNamespace(namespace); | |
cacheRefElement(context.evalNode("cache-ref")); | |
cacheElement(context.evalNode("cache")); | |
parameterMapElement(context.evalNodes("/mapper/parameterMap")); | |
resultMapElements(context.evalNodes("/mapper/resultMap")); | |
sqlElement(context.evalNodes("/mapper/sql")); | |
buildStatementFromContext(context.evalNodes("select|insert|update|delete")); | |
} catch (Exception e) {throw new BuilderException("Error parsing Mapper XML. The XML location is'" + resource + "'. Cause:" + e, e); | |
} | |
} |
buildStatementFromContext()办法:
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {for (XNode context : list) {final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); | |
try {statementParser.parseStatementNode();// 会调用此处, 持续跟进 | |
} catch (IncompleteElementException e) {configuration.addIncompleteStatement(statementParser); | |
} | |
} | |
} |
statementParser.parseStatementNode();
public void parseStatementNode() {String id = context.getStringAttribute("id"); | |
String databaseId = context.getStringAttribute("databaseId"); | |
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {return;} | |
String nodeName = context.getNode().getNodeName(); | |
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); | |
boolean isSelect = sqlCommandType == SqlCommandType.SELECT; | |
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); | |
boolean useCache = context.getBooleanAttribute("useCache", isSelect); | |
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); | |
// Include Fragments before parsing | |
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); | |
includeParser.applyIncludes(context.getNode()); | |
String parameterType = context.getStringAttribute("parameterType"); | |
Class<?> parameterTypeClass = resolveClass(parameterType); | |
String lang = context.getStringAttribute("lang"); | |
LanguageDriver langDriver = getLanguageDriver(lang); | |
// Parse selectKey after includes and remove them. | |
processSelectKeyNodes(id, parameterTypeClass, langDriver); | |
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed) | |
KeyGenerator keyGenerator; | |
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; | |
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); | |
if (configuration.hasKeyGenerator(keyStatementId)) {keyGenerator = configuration.getKeyGenerator(keyStatementId); | |
} else { | |
keyGenerator = context.getBooleanAttribute("useGeneratedKeys", | |
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) | |
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; | |
} | |
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); | |
//SqlSource 是在这里创立的 记住这里等会咱们再来剖析她. 先持续走完流程. | |
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); | |
Integer fetchSize = context.getIntAttribute("fetchSize"); | |
Integer timeout = context.getIntAttribute("timeout"); | |
String parameterMap = context.getStringAttribute("parameterMap"); | |
String resultType = context.getStringAttribute("resultType"); | |
Class<?> resultTypeClass = resolveClass(resultType); | |
String resultMap = context.getStringAttribute("resultMap"); | |
String resultSetType = context.getStringAttribute("resultSetType"); | |
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); | |
if (resultSetTypeEnum == null) {resultSetTypeEnum = configuration.getDefaultResultSetType(); | |
} | |
String keyProperty = context.getStringAttribute("keyProperty"); | |
String keyColumn = context.getStringAttribute("keyColumn"); | |
String resultSets = context.getStringAttribute("resultSets"); | |
// 对各种属性进行了解析并且调用 addMappedStatement 办法. | |
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, | |
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, | |
resultSetTypeEnum, flushCache, useCache, resultOrdered, | |
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); | |
} |
addMappedStatement()办法:
public MappedStatement addMappedStatement( | |
String id, | |
SqlSource sqlSource, | |
StatementType statementType, | |
SqlCommandType sqlCommandType, | |
Integer fetchSize, | |
Integer timeout, | |
String parameterMap, | |
Class<?> parameterType, | |
String resultMap, | |
Class<?> resultType, | |
ResultSetType resultSetType, | |
boolean flushCache, | |
boolean useCache, | |
boolean resultOrdered, | |
KeyGenerator keyGenerator, | |
String keyProperty, | |
String keyColumn, | |
String databaseId, | |
LanguageDriver lang, | |
String resultSets) {if (unresolvedCacheRef) {throw new IncompleteElementException("Cache-ref not yet resolved"); | |
} | |
id = applyCurrentNamespace(id, false); | |
boolean isSelect = sqlCommandType == SqlCommandType.SELECT; | |
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) | |
.resource(resource) | |
.fetchSize(fetchSize) | |
.timeout(timeout) | |
.statementType(statementType) | |
.keyGenerator(keyGenerator) | |
.keyProperty(keyProperty) | |
.keyColumn(keyColumn) | |
.databaseId(databaseId) | |
.lang(lang) | |
.resultOrdered(resultOrdered) | |
.resultSets(resultSets) | |
.resultMaps(getStatementResultMaps(resultMap, resultType, id)) | |
.resultSetType(resultSetType) | |
.flushCacheRequired(valueOrDefault(flushCache, !isSelect)) | |
.useCache(valueOrDefault(useCache, isSelect)) | |
.cache(currentCache); | |
ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); | |
if (statementParameterMap != null) {statementBuilder.parameterMap(statementParameterMap); | |
} | |
MappedStatement statement = statementBuilder.build(); | |
configuration.addMappedStatement(statement);//configuration 外部放入了一个 Map<String, MappedStatement> | |
return statement;// 最终返回 MappedStatement | |
} |
至此,MappedStatement 创立结束
SqlSource 的创立过程
回到 parseStatementNode()办法找到:
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
createSqlSource 办法是 LanguageDriver 接口的一个办法 她有 4 个实现
咱们看一下 xml 实现
public class XMLLanguageDriver implements LanguageDriver { | |
@Override | |
public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql); | |
} | |
@Override | |
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType); | |
return builder.parseScriptNode();// 进行解析, 咱们跟进..} | |
.... | |
} |
public SqlSource parseScriptNode() {MixedSqlNode rootSqlNode = parseDynamicTags(context);// 对动静标签的解决 | |
SqlSource sqlSource;// 初始化 Null | |
if (isDynamic) {// 判断是否动静, 来调用不同的 SqlSource 实例. | |
sqlSource = new DynamicSqlSource(configuration, rootSqlNode); | |
} else {// 跟进 RawSqlSource() | |
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType); | |
} | |
return sqlSource; | |
} |
public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); | |
Class<?> clazz = parameterType == null ? Object.class : parameterType; | |
sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>()); | |
} |
sqlSourceParser.parse()办法要对 sql 进行解决, 这里咱们打个断点, 能够看到原始参数 sql 为
insert into t_test set context = #{context}
parse 办法处理完毕返回:insert into t_test set context = ?
并且调用了 StaticSqlSource 的构造方法
StaticSqlSource 类继实现了 SqlSource 接口,
public class StaticSqlSource implements SqlSource { | |
private final String sql; | |
private final List<ParameterMapping> parameterMappings; | |
private final Configuration configuration; | |
public StaticSqlSource(Configuration configuration, String sql) {this(configuration, sql, null); | |
} | |
public StaticSqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings) { | |
this.sql = sql; | |
this.parameterMappings = parameterMappings; | |
this.configuration = configuration; | |
} | |
@Override | |
public BoundSql getBoundSql(Object parameterObject) {// 这个办法是不是眼生,BoundSql 的构建 | |
return new BoundSql(configuration, sql, parameterMappings, parameterObject); | |
} | |
} |
至此,SqlSource 创立结束.
BoundSql
public class BoundSql { | |
private final String sql; | |
private final List<ParameterMapping> parameterMappings; | |
private final Object parameterObject; | |
private final Map<String, Object> additionalParameters; | |
private final MetaObject metaParameters; | |
........ | |
} |
创立 SqlSource 时, 就把参数放入 BoundSql 类中来构建一个 new BoundSql
public BoundSql getBoundSql(Object parameterObject) {return new BoundSql(configuration, sql, parameterMappings, parameterObject); | |
} |
configuration, sql, parameterMappings, parameterObject 这些参数是十分重要的咱们一一剖析下
configuration
这个不必多说了吧 如果不意识她, 那间接打回新手村
sql
这个属性保留的是咱们在映射文件 xml 中写的 sql 语句
例子就是刚刚解析进去的 Sql 语句:insert into t_test set context = ?
parameterMappings
List<ParameterMapping> parameterMappings;
它是一个 list,每一个元素都是 parameterMapping 对象,这个对象会形容包含属性、名称、表达式、javaType、jdbcType、typeHandler 等重要信息。
ParameterMapping
public class ParameterMapping { | |
private Configuration configuration; | |
private String property; | |
private ParameterMode mode; | |
private Class<?> javaType = Object.class; | |
private JdbcType jdbcType; | |
private Integer numericScale; | |
private TypeHandler<?> typeHandler; | |
private String resultMapId; | |
private String jdbcTypeName; | |
private String expression; | |
...... | |
} |
parameterObject
parameterObject 是参数,能够传递简略对象,pojo、map 或者 @param 注解的参数
传入简略对象
(包含 int、float、double 等),⽐如当咱们传递 int 类型时,MyBatis
会把参数包装成为 Integer 对象传递,相似的 long、float、double 也是如此.
传入 pojo、map
那么这个 parameterObject 参数就是你传入的 POJO 或者 Map 不变
传入多个参数, 不带@Param 注解
那么 MyBatis 就会把
parameterObject 变为一个 Map<String, Object>对象,其键值的关系是按程序来布局
的,相似于这样的模式{”1″:Obj1,”2″:Obj2,”3″:Obj3…}所以在编写的时候咱们都能够应用#{param 1}或者#{1}去引⽤第⼀个参数
传入多个参数, 带 @Param 注解
如果咱们使⽤@Param 注解,那么 MyBatis 就会把 parameterObject 变为一个 Map<String, Object>对象相似于没有@Param 注解,只是把其数字的键值对应置换为了@Param 注解的键值。
⽐如咱们注解
(@Param{“name”} String var1,@Param{“age”} int var2) 数字的键值对应置换为了 name 和 age
关注公众号:java 宝典