非代理使用Mybatis
一、环境搭建
1、pom文件
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mybatis.source.test</groupId>
<artifactId>mybatis-pro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<slf4j-api.version>1.7.25</slf4j-api.version>
<logback.version>1.1.7</logback.version>
</properties>
<dependencies>
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.0.8</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.24.1-GA</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
</dependencies>
</project>
2、Mybatis主配置文件
<?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>
<settings>
<setting name="lazyLoadingEnabled" value="false"/>
</settings>
<typeAliases>
<typeAlias alias="role" type="com.mybatis.learn.bean.Role"/>
</typeAliases>
<typeHandlers>
<typeHandler jdbcType="VARCHAR" javaType="string" handler="com.mybatis.learn.handler.MyStringHandler"/>
</typeHandlers>
<!-- 定义数据库的信息,默认使用development数据库构建环境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/project_crowd?serverTimezone=UTC" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!-- 定义映射器 -->
<mappers>
<package name="com.mybatis.learn.mapper" />
</mappers>
</configuration>
3、日志配置文件
log4j.rootLogger=DEBUG,stdout
log4j.logger.org.mybatis=DUBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.logDailyFile.layout.ConversionPattern = %5p %d %C:%m%n
4、mapper配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.source.test.mapper.RoleMapper">
<resultMap type="role" id="roleMap">
<id column="id" property="id" javaType="long" jdbcType="BIGINT" />
<result column="role_name" property="roleName" javaType="string" jdbcType="VARCHAR" />
<result column="note" property="note" typeHandler="com.mybatis.source.test.handler.MyStringHandler" />
</resultMap>
<select id="queryRolesByCondition" parameterType="com.mybatis.source.test.bean.Role" resultMap="roleMap">
select * from role where
1=1
<if test="id!=null">
and id=#{id,jdbcType=BIGINT}
</if>
<if test="roleName!=null">
and role_name=#{roleName,jdbcType=BIGINT}
</if>
<if test="note!=null">
and note=#{note,jdbcType=BIGINT}
</if>
and tenant_id=#{tenantId,jdbcType=VARCHAR}
</select>
<select id="getRole" parameterType="long" resultMap="roleMap">
select
id,role_name as roleName,note from role where id=#{id}
</select>
<select id="findRole" parameterType="long" resultMap="roleMap">
select
id,role_name,note from role where role_name like CONCAT('%',#{roleName
javaType=string,
jdbcType=VARCHAR,typeHandler=com.mybatis.source.test.handler.MyStringHandler},'%')
</select>
<insert id="insertRole" parameterType="role">
insert into
role(role_name,note) value(#{roleName},#{note})
</insert>
<delete id="deleteRole" parameterType="long">
delete from role where
id=#{id}
</delete>
</mapper>
5、mapper接口
package com.mybatis.source.test.mapper;
import java.util.List;
import com.mybatis.source.test.bean.Role;
public interface RoleMapper {
public Role getRole(Long id);
public Role findRole(String roleName);
public int deleteRole(Long id);
public int insertRole(Role role);
public List<Role> queryRolesByCondition(Role role);
}
二、非代理方式代码执行
1、测试代码
package com.mybatis.source.test.Main;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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 com.mybatis.source.test.bean.Role;
import com.mybatis.source.test.mapper.RoleMapper;
public class Main {
public static void main(String[] args) {
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = null;
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = null;
try {
sqlSession = sqlSessionFactory.openSession();
Map<String,Object> param=new HashMap<>();
param.put("id", 1);
List<Role> list = sqlSession.selectList("com.mybatis.source.test.mapper.RoleMapper.queryRolesByCondition", param);
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
e.printStackTrace();
} finally {
sqlSession.close();
}
}
}
2、执行结果
com.mybatis.source.test.mapper.RoleMapper.queryRolesByCondition - ==>
Preparing: select * from role where 1=1 and id=? and tenant_id=?
com.mybatis.source.test.mapper.RoleMapper.queryRolesByCondition - ==> Parameters: 1(Integer), null
com.mybatis.source.test.mapper.RoleMapper.queryRolesByCondition - <== Total: 0
3、结果分析
RoleMapper.queryRolesByCondition接口的入参是Role,但是在测试代码中,我们传入的是Map对象,发现代码也能运行。
其原因是因为参数值的获取是通过MetaObject工具类来获取的,代码如下:
//设置参数
@Override
public void setParameters(PreparedStatement ps) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
//循环设参数
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
//如果不是OUT,才设进去
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
//若有额外的参数, 设为额外的参数
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
//若参数为null,直接设null
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
//若参数有相应的TypeHandler,直接设object
value = parameterObject;
} else {
//除此以外,MetaObject.getValue反射取得值设进去
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
//不同类型的set方法不同,所以委派给子类的setParameter方法
jdbcType = configuration.getJdbcTypeForNull();
}
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
4、MetaObject测试
采用MetaObject工具类,并不关心对象是什么样的类型,只要给出对象中有什么属性,就可以通过工具类拿到对象对应的属性的值
package com.mybatis.learn.main;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
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;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class Person{
private String name;
private String address;
public Person(String name, String address) {
this.name = name;
this.address = address;
}
}
public class MetaObjectTest {
static SqlSessionFactory sqlSessionFactory = null;
static{
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
public static void main(String[] args) {
List<String> fieldNameList=new ArrayList<>();
fieldNameList.add("name");
fieldNameList.add("address");
Map<String,String> map=new HashMap<>();
map.put("name","张三");
map.put("address","杭州市");
//可以访问map中属性为name与address的值
printFieldValue(map,fieldNameList);
System.out.println("+++++++++++++++++++++++++");
Person person=new Person("王五","北京市");
// 可以访问对象中属性为name与address的值
printFieldValue(person,fieldNameList);
}
public static void printFieldValue(Object obj, List<String> fieldNames){
SqlSession sqlSession = sqlSessionFactory.openSession();
Configuration configuration = sqlSession.getConfiguration();
MetaObject metaObject=configuration.newMetaObject(obj);
for (String fieldName:fieldNames){
Object value = metaObject.getValue(fieldName);
System.out.println(value);
}
}
// 模拟MetaObject对象的getValue方法
public static void printFieldByReflect(Object obj, List<String> fieldNames){
if(obj instanceof Map){
Map<String,Object> map=( Map<String,Object>)obj;
for(String fieldName:fieldNames){
System.out.println(map.get(fieldName));
}
}else if(obj instanceof Collection){
}else if(obj != null && obj.getClass().isArray()){
}else{
Class<?> aClass = obj.getClass();
for(String fieldName:fieldNames){
try{
Field field = aClass.getDeclaredField(fieldName);
field.setAccessible(true);
Object o = field.get(obj);
System.out.println(o);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
所以在Mybatis对象设置参数值时,一般并不会去校验对象的类型,只关注对象中是否有相应的属性。