int类型初始值和mybatis的list、map

本文探讨了MyBatis中int类型的一个常见问题:当前端未传递参数时,MyBatis默认填充0值,导致数据库字段被误更新。通过将int类型更改为Integer可以解决此问题。此外还提到了处理List或Map类型时需要注意的情况。

int类型真的是有毒,前端什么都不传入的时候,自己默认一个0,然后更新的mapper里判断的是不为null。。。每次都把这个值更新成0,简直了。。。

把int换成integer就没有问题了

 

mybatis里如果字段类型是list或者map时,有时一定要传入一个空值,然后会报sql错误,然后报的那个sql错误还有错这是最毒的。。。。

<think>我们正在讨论MyBatis的反射底层原理。根据之前的引用,我们知道MyBatis在底层使用了反射机制,特别是在Mapper接口与XML映射文件的绑定过程中。现在,我们需要深入探讨MyBatis如何利用反射来实现这些功能。 关键点: 1. MyBatis通过动态代理创建Mapper接口的代理对象。 2. 当调用Mapper接口的方法时,实际上调用的是代理对象的方法,代理对象会拦截方法调用,并根据方法名参数去执行相应的SQL。 3. 反射在MyBatis中的应用主要包括: - 通过反射获取Mapper接口的方法信息(方法名、参数类型、返回类型等)。 - 利用反射调用方法(在结果映射时,将查询结果通过反射设置到实体对象的属性中)。 根据引用[3]:MyBatis底层通过反射机制完成了mapper接口方法xml映射文件标签的绑定。也就是说,每一个Mapper接口中的方法都对应MappedStatement对象(存储在Configuration中),而MappedStatement包含了SQL语句、参数映射、结果映射等信息。 具体步骤: 1. 在初始化阶段,MyBatis会解析所有的Mapper.xml文件,将每个SQL语句封装成MappedStatement对象,并以一个唯一的ID(通常是接口全限定名+方法名)存储到Configuration对象中。 2. 当获取Mapper接口的代理对象时,使用动态代理(JDK动态代理)生成代理实例。这个代理实例由MapperProxy类处理。 3. 当调用Mapper接口方法时,代理对象会拦截调用,并交给MapperProxy的invoke方法处理。 4. MapperProxy根据调用的方法信息(方法名参数)构建一个MapperMethod对象(如果缓存中没有的话),然后执行MapperMethod的execute方法。 5. 在MapperMethod中,根据方法返回类型(由反射获取)决定执行SQL的方式(selectOne, selectList, insert, update等)。 6. 执行SQL后,利用反射将结果集映射到Java对象(通过反射创建对象,并设置属性值)。 反射在结果映射中的使用: - 结果集映射时,MyBatis通过反射创建实体类的实例(调用无参构造器)。 - 然后通过反射调用实体类的setter方法(或直接设置字段,如果配置为使用字段而不是setter)来设置属性值。 另外,参数传递时,反射用于获取方法的参数名(如果使用注解@Param)或参数类型,以便将参数设置到SQL语句中。 因此,我们可以将MyBatis反射底层原理分为两个主要部分: 1. 接口方法的代理调用(动态代理+反射) 2. 结果集到Java对象的映射(反射) 下面我们具体展开: 一、动态代理创建Mapper接口的代理对象 MyBatis使用JDK动态代理为Mapper接口创建代理对象。核心类是MapperProxyFactoryMapperProxy。 二、反射在方法调用结果映射中的应用 - 方法调用:通过反射获取方法签名(方法名、参数类型、返回类型)来定位对应的SQL语句。 - 结果映射:通过反射创建对象并设置属性。 三、反射在参数处理中的应用 - 将Java方法的参数转换为SQL语句的参数。 代码示例(模拟MyBatis反射使用): 由于MyBatis源码较复杂,我们简化模拟: 步骤1:创建代理对象 ```java public class MapperProxyFactory<T> { private final Class<T> mapperInterface; public MapperProxyFactory(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface); return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy); } } ``` 步骤2:实现InvocationHandler(MapperProxy) ```java public class MapperProxy<T> implements InvocationHandler { private final SqlSession sqlSession; private final Class<T> mapperInterface; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 如果方法是Object类的方法,直接调用(如toString, hashCode等) if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } // 根据方法名接口类型,定位到要执行的SQL(在MyBatis中是通过MapperMethod来执行的) // 这里简化:直接调用SqlSession的方法 // 实际上,MyBatis会使用MapperMethod来统一处理 // 我们假设方法名就是SQL语句的id(全限定名+方法名) String statement = mapperInterface.getName() + "." + method.getName(); // 根据返回类型选择执行的方法 Class<?> returnType = method.getReturnType(); if (List.class.isAssignableFrom(returnType)) { return sqlSession.selectList(statement, args == null ? null : args[0]); } else { return sqlSession.selectOne(statement, args == null ? null : args[0]); } } } ``` 步骤3:结果映射(反射创建对象设置属性) 在sqlSession.selectOne方法内部,会执行SQL,然后使用ResultSetHandler处理结果集。在ResultSetHandler中,使用反射创建对象并设置属性。 示例代码(简化): ```java public class DefaultResultSetHandler { public <E> List<E> handleResultSets(Statement stmt) throws SQLException { List<E> results = new ArrayList<>(); ResultSet rs = stmt.getResultSet(); while (rs.next()) { // 通过反射创建实体类实例(假设我们已知实体类类型) E entity = (E) entityClass.newInstance(); // 遍历结果集的列 ResultSetMetaData metaData = rs.getMetaData(); int columnCount = metaData.getColumnCount(); for (int i = 1; i <= columnCount; i++) { String columnName = metaData.getColumnName(i); Object value = rs.getObject(i); // 通过反射设置属性(这里简单使用字段,实际MyBatis可以配置setter方法) Field field = entityClass.getDeclaredField(columnName); field.setAccessible(true); field.set(entity, value); } results.add(entity); } return results; } } ``` 注意:实际MyBatis的结果映射更复杂,支持构造函数映射、嵌套映射等,但基本原理都是反射。 四、反射获取方法参数名 在Java8之前,无法通过反射直接获取方法参数名(会得到arg0, arg1这样的名字)。MyBatis提供了@Param注解来指定参数名。在Java8之后,可以通过编译时加入参数表(-parameters)来获取真实参数名。MyBatis中通过ParamNameResolver来解析参数名。 总结: MyBatis的反射底层原理主要体现在: 1. 动态代理Mapper接口,拦截方法调用。 2. 利用反射获取方法信息(方法名、参数类型、返回类型)来定位SQL。 3. 通过反射创建结果对象并设置属性值。 4. 通过反射解析方法参数名,将参数传递给SQL。 因此,反射是MyBatis实现ORM映射动态代理调用的核心技术之一。 引用说明: [^3] 说明了MyBatis通过反射完成接口方法XML映射的绑定。 [^4] 提到了JDBC的基本操作,而MyBatis底层基于JDBC,反射用于将结果集映射到对象。 相关问题: 1. MyBatis中如何解决反射设置属性时的性能问题? 2. MyBatis除了反射还使用了哪些技术来实现ORM映射? 3. MyBatisMapper接口没有实现类,它是如何被实例化的? 4. MyBatis在结果映射时如何处理复杂的嵌套关系? </think> ### MyBatis反射底层原理解析 MyBatis的反射底层原理主要涉及**动态代理生成Mapper接口实现****反射实现对象映射**两大核心机制,以下是详细解析: #### 一、Mapper接口动态代理原理 1. **代理对象生成** MyBatis通过`MapperProxyFactory`创建Mapper接口的代理对象(JDK动态代理): ```java public class MapperProxyFactory<T> { public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface); return (T) Proxy.newProxyInstance( mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy // 调用处理器 ); } } ``` 2. **方法调用拦截** 代理对象的方法调用被`MapperProxy.invoke()`拦截处理: ```java public class MapperProxy<T> implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 1. 获取MapperMethod(缓存中获取或新建) MapperMethod mapperMethod = cachedMapperMethod(method); // 2. 执行SQL并返回结果 return mapperMethod.execute(sqlSession, args); } } ``` 3. **SQL执行映射** `MapperMethod`将接口方法映射为具体SQL操作: ```java public class MapperMethod { public Object execute(SqlSession sqlSession, Object[] args) { switch (command.getType()) { case SELECT: return sqlSession.selectOne(command.getName(), param); case INSERT: return sqlSession.insert(command.getName(), param); // UPDATE/DELETE 同理 } } } ``` [^3] #### 二、反射实现对象映射 1. **结果集映射** 通过反射将`ResultSet`转换为Java对象: ```java public class DefaultResultSetHandler { private Object createResultObject(ResultSet rs, Class<?> resultType) { Object resultObject = resultType.newInstance(); // 反射创建对象 ResultSetMetaData metaData = rs.getMetaData(); for (int i = 1; i <= metaData.getColumnCount(); i++) { String column = metaData.getColumnName(i); Field field = resultType.getDeclaredField(column); field.setAccessible(true); field.set(resultObject, rs.getObject(column)); // 反射设置属性值 } return resultObject; } } ``` [^4] 2. **参数映射** 方法参数通过`ParamNameResolver`解析为SQL参数: ```java public class ParamNameResolver { public Object getNamedParams(Object[] args) { // 反射获取参数名(Java8+的Parameter或@Param注解) String[] paramNames = getParamNames(method); Map<String, Object> paramMap = new HashMap<>(); for (int i = 0; i < args.length; i++) { paramMap.put(paramNames[i], args[i]); } return paramMap; } } ``` #### 三、核心反射组件 | 组件 | 作用 | 反射应用点 | |---------------------|-----------------------------------|------------------------------| | `MapperProxy` | 动态代理处理器 | 拦截接口方法调用 | | `MapperMethod` | 方法到SQL的映射 | 解析方法签名返回类型 | | `ResultSetHandler` | 结果集处理 | 反射创建对象+属性赋值 | | `ParamNameResolver` | 参数名解析 | 获取方法参数名(反射/注解) | | `Reflector` | MyBatis自带的反射工具类 | 缓存类元数据提升性能 | #### 四、性能优化策略 1. **元数据缓存** `Reflector`类缓存Class的字段/方法信息,避免重复反射: ```java public class Reflector { private static final Map<Class<?>, Reflector> REFLECTOR_MAP = new ConcurrentHashMap<>(); public Reflector(Class<?> clazz) { // 缓存字段/getter/setter等元数据 } } ``` 2. **字节码增强** 使用`javassist`生成`get/set`方法的字节码,替代反射调用(可选优化)。 3. **类型处理器** 内置`TypeHandler`处理特定类型的转换(如Date→Timestamp),减少反射调用[^2]。 #### 五、执行流程示例 1. 调用`userMapper.getUserById(1)` 2. `MapperProxy`拦截方法调用 3. `MapperMethod`解析为`SELECT`操作 4. 执行SQL:`SELECT * FROM user WHERE id = ?` 5. `ResultSetHandler`通过反射创建User对象 6. 遍历ResultSet,反射设置`user.setId()`/`user.setName()` 7. 返回User对象 > **数学表达**:设$M$为Mapper接口,$m$为接口方法,$P$为参数,则代理执行过程可表示为: > $$ \text{ProxyCall}(M.m(P)) = \text{SqlSession} \cdot \text{execute}( \text{Map}(m), \text{Resolve}(P) ) $$
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值