添加数据库操作模板 对于JDBC操作,一般包括以下几个步骤: 1)注册驱动 2)建立连接 3)执行sql语句 4)处理结果 5)释放资源 上面这些步骤,真正和我们处理相关的,是第三步和第四步,其他步骤,都是通用的逻辑,因此,我们可以将这些步骤抽象成一个模板方法类,其内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package com.yang.mybatis.session; import com.yang.mybatis.config.MybatisDataSource; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public abstract class TransactionInvoke<T> { public T invoke(MybatisDataSource mybatisDataSource) { String username = mybatisDataSource.getUsername(); String password = mybatisDataSource.getPassword(); String url = mybatisDataSource.getUrl(); String driver = mybatisDataSource.getDriver(); Connection connection = null ; T result = null ; try { Class.forName(driver); connection = DriverManager.getConnection(url, username, password); connection.setAutoCommit(false ); result = execute(connection); connection.commit(); } catch (SQLException e) { try { connection.rollback(); } catch (SQLException ex) { throw new RuntimeException(ex); } } catch (ClassNotFoundException e) { throw new RuntimeException(e); } finally { closeResource(connection); } return result; } private void closeResource(Connection connection) { if (connection != null ) { try { connection.close(); } catch (SQLException e) { throw new RuntimeException(e); } } } public abstract T execute(Connection connection) throws SQLException;}
因为涉及到数据库操作,所以我们要先引入mysql的依赖:
1 2 3 4 5 <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0 .27 </version> </dependency>
查询某个字段 首先,我们修改DefaultMybatisSqlSession中,在该类中,执行我们的sql语句,其中,执行sql和对sql查询结果进行处理的内容,收敛在execute方法中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 package com.yang.mybatis.session; import com.yang.mybatis.config.MybatisConfiguration; import com.yang.mybatis.config.MybatisEnvironment; import com.yang.mybatis.config.MybatisSqlStatement; import com.yang.mybatis.proxy.MapperProxyFactory; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Map; public class DefaultMybatisSqlSession implements IMybatisSqlSession { private MapperProxyFactory mapperProxyFactory; private MybatisConfiguration mybatisConfiguration; public DefaultMybatisSqlSession(MapperProxyFactory mapperProxyFactory) { this.mapperProxyFactory = mapperProxyFactory; this.mybatisConfiguration = mapperProxyFactory.getMybatisConfiguration(); } @Override public <T> T execute(String method, Object parameter) { Map<String, MybatisSqlStatement> mapperMethod2SqlStatementsMap = mapperProxyFactory.getMybatisConfiguration().getMapperMethod2SqlStatementsMap(); MybatisSqlStatement mybatisSqlStatement = mapperMethod2SqlStatementsMap.get(method); MybatisEnvironment defaultMybatisEnvironment = this.mybatisConfiguration.getDefaultMybatisEnvironment(); return new TransactionInvoke<T>() { @Override public T execute(Connection connection) throws SQLException { String rawSql = mybatisSqlStatement.getSql(); List<String> parameterNameList = new ArrayList<>(); String sql = extractRawSql(rawSql, parameterNameList); Object[ ] parameters = (Object[ ] ) parameter; PreparedStatement preparedStatement = connection.prepareStatement(sql); if (parameterNameList.size() != parameters.length) { throw new RuntimeException("SQL语句参数个数不匹配====" ); } int index = 1 ; for (Object o : parameters) { preparedStatement.setObject(index ++, o); } ResultSet resultSet = preparedStatement.executeQuery(); T result = null ; if (resultSet.next()) { result = (T) resultSet.getObject(1 ); } resultSet.close(); return result; } } .invoke(defaultMybatisEnvironment.getMybatisDataSource()); } private String extractRawSql(String rawSql, List<String> parameterNameList) { StringBuilder sqlBuilder = new StringBuilder(); int start = 0 ; int end = -1 ; while ((end = rawSql.indexOf("#" , start)) != -1 ) { sqlBuilder.append(rawSql.substring(start, end - 1 )) .append(" ? " ); int parameterStart = end + 2 ; int parameterEnd = rawSql.indexOf("}" , parameterStart); parameterNameList.add(rawSql.substring(parameterStart, parameterEnd)); start = parameterEnd + 1 ; } sqlBuilder.append(rawSql.substring(start)); return sqlBuilder.toString(); } @Override public <T> T getMapper(Class<T> type) { return (T) mapperProxyFactory.newInstance(type, this); } }
最后我们添加测试方法,进行测试:
1 2 3 4 5 6 7 8 9 10 11 String configPath = "mybatis-config.xml" ; IMybatisSqlSessionFactory mybatisSqlSessionFactory = new MybatisSqlSessionFactoryBuilder() .setMybatisMapperParser(new XmlMybatisMapperParser()) .setMybatisConfigurationParser(new XmlMybatisConfigurationParser()) .setConfigPath(configPath) .buildSqlSessionFactory(); IMybatisSqlSession mybatisSqlSession = mybatisSqlSessionFactory.openSession(); IUserMapper userMapper = mybatisSqlSession.getMapper(IUserMapper.class); System.out.println(userMapper.queryUserName(1 ));
测试结果如下:
查询结果封装为对象 基于resultType 上述的操作,只对查询某个字段有效,假设我们要获取的是一个对象,比如我们在UserMapper添加如下方法:
1 2 User queryUserById(Integer id);
其中,User类内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public class User implements Serializable { private Integer id; private String userName; private String password; private Integer age; private LocalDateTime createTime; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public LocalDateTime getCreateTime() { return createTime; } public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; } }
该方法对于的mapperxml查询块如下:
1 2 3 4 <select id="queryUserById" resultType="com.yang.mybatis.test.User" > select * from user where id = #{ id} </select>
此时,当我们使用JDBC执行sql,获取ResultSet后,我们可以根据resultType的类型,通过反射的方式,来创建对应的结果,并将属性填充到对象中。 首先,我们修改MybatisSqlStatement,添加resultType字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.yang.mybatis.config; import java.io.Serializable; public class MybatisSqlStatement implements Serializable { private String namespace; private String id; private String sql; private String resultType; public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getSql() { return sql; } public void setSql(String sql) { this.sql = sql; } public String getResultType() { return resultType; } public void setResultType(String resultType) { this.resultType = resultType; } }
然后修改XmlMybatisMapperParser的parseStatement方法,设置对应的resultType值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private void parseStatement(List<MybatisSqlStatement> mybatisSqlStatements, List<Element> elements, Element root) { if (elements == null || elements.isEmpty()) { return; } String namespace = root.attributeValue("namespace" ); for (Element element : elements) { String id = element.attributeValue("id" ); String resultType = element.attributeValue("resultType" ); String sql = element.getText().trim(); MybatisSqlStatement mybatisSqlStatement = new MybatisSqlStatement(); mybatisSqlStatement.setNamespace(namespace); mybatisSqlStatement.setId(id); mybatisSqlStatement.setSql(sql); mybatisSqlStatement.setResultType(resultType); mybatisSqlStatements.add(mybatisSqlStatement); } }
最后修改DefaultMybatisSqlSession:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 package com.yang.mybatis.session; import com.google.common.base.CaseFormat; import com.yang.mybatis.config.MybatisConfiguration; import com.yang.mybatis.config.MybatisEnvironment; import com.yang.mybatis.config.MybatisSqlStatement; import com.yang.mybatis.proxy.MapperProxyFactory; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class DefaultMybatisSqlSession implements IMybatisSqlSession { private MapperProxyFactory mapperProxyFactory; private MybatisConfiguration mybatisConfiguration; public DefaultMybatisSqlSession(MapperProxyFactory mapperProxyFactory) { this.mapperProxyFactory = mapperProxyFactory; this.mybatisConfiguration = mapperProxyFactory.getMybatisConfiguration(); } @Override public <T> T execute(String method, Object parameter) { Map<String, MybatisSqlStatement> mapperMethod2SqlStatementsMap = mapperProxyFactory.getMybatisConfiguration().getMapperMethod2SqlStatementsMap(); MybatisSqlStatement mybatisSqlStatement = mapperMethod2SqlStatementsMap.get(method); MybatisEnvironment defaultMybatisEnvironment = this.mybatisConfiguration.getDefaultMybatisEnvironment(); return new TransactionInvoke<T>() { @Override public T execute(Connection connection) throws SQLException { String rawSql = mybatisSqlStatement.getSql(); List<String> parameterNameList = new ArrayList<>(); String sql = extractRawSql(rawSql, parameterNameList); Object[ ] parameters = (Object[ ] ) parameter; PreparedStatement preparedStatement = connection.prepareStatement(sql); if (parameterNameList.size() != parameters.length) { throw new RuntimeException("SQL语句参数个数不匹配====" ); } int index = 1 ; for (Object o : parameters) { preparedStatement.setObject(index ++, o); } ResultSet resultSet = preparedStatement.executeQuery(); T result = parseResult(resultSet, mybatisSqlStatement); resultSet.close(); return result; } } .invoke(defaultMybatisEnvironment.getMybatisDataSource()); } private <T> T parseResult(ResultSet resultSet, MybatisSqlStatement mybatisSqlStatement) throws SQLException { String resultType = mybatisSqlStatement.getResultType(); if (resultType == null || resultType.isEmpty()) { return (T) resultSet.getObject(1 ); } try { Class<?> aClass = Class.forName(resultType); Field[ ] fields = aClass.getDeclaredFields(); Map<String, String> fieldName2ColumnNameMap = new HashMap<>(); Map<String, Field> fieldName2FieldMap = new HashMap<>(); for (Field field : fields) { String columnName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName()); fieldName2ColumnNameMap.put(field.getName(), columnName); fieldName2FieldMap.put(field.getName(), field); } Object result = aClass.newInstance(); while (resultSet.next()) { for (Map.Entry<String, String> entry : fieldName2ColumnNameMap.entrySet()) { String fieldName = entry.getKey(); String columnName = entry.getValue(); Object columnValue = resultSet.getObject(columnName); Field field = fieldName2FieldMap.get(fieldName); field.setAccessible(true ); field.set(result, columnValue); } } return (T)result; } catch (ClassNotFoundException e) { throw new RuntimeException(e); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } private String extractRawSql(String rawSql, List<String> parameterNameList) { StringBuilder sqlBuilder = new StringBuilder(); int start = 0 ; int end = -1 ; while ((end = rawSql.indexOf("#" , start)) != -1 ) { sqlBuilder.append(rawSql.substring(start, end - 1 )) .append(" ? " ); int parameterStart = end + 2 ; int parameterEnd = rawSql.indexOf("}" , parameterStart); parameterNameList.add(rawSql.substring(parameterStart, parameterEnd)); start = parameterEnd + 1 ; } sqlBuilder.append(rawSql.substring(start)); return sqlBuilder.toString(); } @Override public <T> T getMapper(Class<T> type) { return (T) mapperProxyFactory.newInstance(type, this); } }
这里将和结果相关的解析,抽取到parseResult方法中,此外,因为数据库是字段是下划线格式,类的属性是驼峰格式,因此,这里引入了Guava依赖,方便使用它的CaseFormat类进行格式转化。
1 2 3 4 5 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.1 -jre</version> </dependency>
最后,我们添加测试方法,进行测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static void main(String[ ] args) { String configPath = "mybatis-config.xml" ; IMybatisSqlSessionFactory mybatisSqlSessionFactory = new MybatisSqlSessionFactoryBuilder() .setMybatisMapperParser(new XmlMybatisMapperParser()) .setMybatisConfigurationParser(new XmlMybatisConfigurationParser()) .setConfigPath(configPath) .buildSqlSessionFactory(); IMybatisSqlSession mybatisSqlSession = mybatisSqlSessionFactory.openSession(); IUserMapper userMapper = mybatisSqlSession.getMapper(IUserMapper.class); User user = userMapper.queryUserById(1 ); System.out.println(user); System.out.println(user.getId()); System.out.println(user.getUserName()); System.out.println(user.getPassword()); System.out.println(user.getAge()); System.out.println(user.getCreateTime()); }
测试结果如下:
基于resultMap 上面的方式,是基于resultType来进行解析的,但是在mybatis中,还有另外一种将sql字段和类对象属性映射的方式,就是resultMap。 首先,我们创建一个IdUserNameVO类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.yang.mybatis.test; import java.io.Serializable; public class IdUserNameVO implements Serializable { private Integer id; private String userName; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }
在IUserMapper中,我们添加下列方法:
1 2 IdUserNameVO queryIdUserNameVOById(Integer id);
修改UserMapper.xml,加上queryIdUserNameVOById的sql语句和对应的resultMap
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?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.yang.mybatis.test.IUserMapper" > <resultMap id="idUserName" type="com.yang.mybatis.test.IdUserNameVO" > <id property="id" column="id" javaType="Integer" jdbcType="int" /> <result property="userName" column="user_name" javaType="String" jdbcType="VARCHAR" /> </resultMap> <select id="queryUserName" > select user_name from user where id = #{ id} </select> <select id="queryUserAge" > select age from user where id = #{ id} </select> <select id="queryUserById" resultType="com.yang.mybatis.test.User" > select * from user where id = #{ id} </select> <select id="queryIdUserNameVOById" resultMap="idUserName" > select id, user_name from user where id = #{ id} </select> </mapper>
之前,我们在解析每一个mapper.xml文件时,解析出来的结果,是一个MybatisSqlStatement列表,但是这种方式还不能更好的表达一个mapper.xml中包含的信息,因此,我们修改代码,现在对于每一个mapper.xml文件,解析出来的结果位MybatisMapperXmlConfiguration类,该类定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.yang.mybatis.config; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class MybatisMapperXmlConfiguration implements Serializable { private String mapperName; private List<MybatisSqlStatement> mybatisSqlStatements = new ArrayList<>(); private Map<String, MybatisResultMap> mybatisResultMaps = new HashMap<>(); public List<MybatisSqlStatement> getMybatisSqlStatements() { return mybatisSqlStatements; } public void setMybatisSqlStatements(List<MybatisSqlStatement> mybatisSqlStatements) { this.mybatisSqlStatements = mybatisSqlStatements; } public List<MybatisResultMap> getMybatisResultMaps() { return new ArrayList<>(mybatisResultMaps.values()); } public void addMybatisSqlStatement(MybatisSqlStatement mybatisSqlStatement) { this.mybatisSqlStatements.add(mybatisSqlStatement); } public void addMybatisResultMap(MybatisResultMap mybatisResultMap) { this.mybatisResultMaps.put(mybatisResultMap.getId(), mybatisResultMap); } public MybatisResultMap getMybatisResultMap(String id) { return this.mybatisResultMaps.get(id); } public String getMapperName() { return mapperName; } public void setMapperName(String mapperName) { this.mapperName = mapperName; } }
MybatisResultMap的定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.yang.mybatis.config; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public class MybatisResultMap implements Serializable { private String id; private String type; private MybatisResultMapProperty idProperty; private List<MybatisResultMapProperty> properties = new ArrayList<>(); public void addMybatisResultMapProperty(MybatisResultMapProperty mybatisResultMapProperty) { this.properties.add(mybatisResultMapProperty); } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getType() { return type; } public void setType(String type) { this.type = type; } public MybatisResultMapProperty getIdProperty() { return idProperty; } public void setIdProperty(MybatisResultMapProperty idProperty) { this.idProperty = idProperty; } public List<MybatisResultMapProperty> getProperties() { return this.properties; } }
MybatisResultMapProperty类定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.yang.mybatis.config; import java.io.Serializable; public class MybatisResultMapProperty implements Serializable { private String property; private String column; private String javaType; private String jdbcType; ... 省略getter和setter}
我们修改IMybatisMapperParser接口:
1 2 3 4 5 6 7 8 package com.yang.mybatis.config.parser; import com.yang.mybatis.config.MybatisMapperXmlConfiguration; public interface IMybatisMapperParser { MybatisMapperXmlConfiguration parseMapper(String path);}
修改MybatisStatement,加上resultMap属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.yang.mybatis.config; import java.io.Serializable; public class MybatisSqlStatement implements Serializable { private String namespace; private String id; private String sql; private String resultType; private String resultMap; public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getSql() { return sql; } public void setSql(String sql) { this.sql = sql; } public String getResultType() { return resultType; } public void setResultType(String resultType) { this.resultType = resultType; } public String getResultMap() { return resultMap; } public void setResultMap(String resultMap) { this.resultMap = resultMap; } }
修改其IMybatisMapperParser具体实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 package com.yang.mybatis.config.parser; import com.yang.mybatis.config.MybatisMapperXmlConfiguration; import com.yang.mybatis.config.MybatisResultMap; import com.yang.mybatis.config.MybatisResultMapProperty; import com.yang.mybatis.config.MybatisSqlStatement; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.InputStream; import java.util.HashSet; import java.util.List; import java.util.Set; public class XmlMybatisMapperParser implements IMybatisMapperParser { private final static Set<String> tagSet = new HashSet<>(); private final static Set<String> resultMapTagSet = new HashSet<>(); static { tagSet.add("select" ); tagSet.add("insert" ); tagSet.add("update" ); tagSet.add("delete" ); tagSet.add("SELECT" ); tagSet.add("INSERT" ); tagSet.add("UPDATE" ); tagSet.add("DELETE" ); resultMapTagSet.add("resultMap" ); resultMapTagSet.add("ResultMap" ); } @Override public MybatisMapperXmlConfiguration parseMapper(String path) { MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration = new MybatisMapperXmlConfiguration(); try { InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(path); SAXReader saxReader = new SAXReader(); Document document = saxReader.read(inputStream); Element root = document.getRootElement(); parseMapperName(mybatisMapperXmlConfiguration, root); for (String tag : tagSet) { List<Element> elements = root.elements(tag); parseStatement(mybatisMapperXmlConfiguration, elements); } for (String tag: resultMapTagSet) { List<Element> elements = root.elements(tag); parseResultMap(mybatisMapperXmlConfiguration, elements); } } catch (DocumentException e) { e.printStackTrace(); } return mybatisMapperXmlConfiguration; } private void parseMapperName(MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration, Element root) { String mapperName = root.attributeValue("namespace" ); mybatisMapperXmlConfiguration.setMapperName(mapperName); } private void parseResultMap(MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration, List<Element> elements) { if (elements == null || elements.isEmpty()) { return; } for (Element element : elements) { String id = element.attributeValue("id" ); String type = element.attributeValue("type" ); MybatisResultMap mybatisResultMap = new MybatisResultMap(); mybatisResultMap.setId(id); mybatisResultMap.setType(type); Element idElement = element.element("id" ); if (idElement != null ) { MybatisResultMapProperty mybatisResultMapProperty = buildMybatisResultMapProperty(idElement); mybatisResultMap.addMybatisResultMapProperty(mybatisResultMapProperty); mybatisResultMap.setIdProperty(mybatisResultMapProperty); } List<Element> resultList = element.elements("result" ); for (Element resultElement : resultList) { MybatisResultMapProperty mybatisResultMapProperty = buildMybatisResultMapProperty(resultElement); mybatisResultMap.addMybatisResultMapProperty(mybatisResultMapProperty); } mybatisMapperXmlConfiguration.addMybatisResultMap(mybatisResultMap); } } private MybatisResultMapProperty buildMybatisResultMapProperty(Element element) { MybatisResultMapProperty mybatisResultMapProperty = new MybatisResultMapProperty(); String property = element.attributeValue("property" ); String column = element.attributeValue("column" ); String javaType = element.attributeValue("javaType" ); String jdbcType = element.attributeValue("jdbcType" ); mybatisResultMapProperty.setProperty(property); mybatisResultMapProperty.setColumn(column); mybatisResultMapProperty.setJavaType(javaType); mybatisResultMapProperty.setJdbcType(jdbcType); return mybatisResultMapProperty; } private void parseStatement(MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration, List<Element> elements) { if (elements == null || elements.isEmpty()) { return; } String namespace = mybatisMapperXmlConfiguration.getMapperName(); for (Element element : elements) { String id = element.attributeValue("id" ); String resultType = element.attributeValue("resultType" ); String resultMap = element.attributeValue("resultMap" ); String sql = element.getText().trim(); MybatisSqlStatement mybatisSqlStatement = new MybatisSqlStatement(); mybatisSqlStatement.setNamespace(namespace); mybatisSqlStatement.setId(id); mybatisSqlStatement.setSql(sql); mybatisSqlStatement.setResultType(resultType); mybatisSqlStatement.setResultMap(resultMap); mybatisMapperXmlConfiguration.addMybatisSqlStatement(mybatisSqlStatement); } } }
修改MybatisSqlSessionFactoryBuilder类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package com.yang.mybatis.session; import com.yang.mybatis.config.MybatisConfiguration; import com.yang.mybatis.config.MybatisMapperXmlConfiguration; import com.yang.mybatis.config.MybatisSqlStatement; import com.yang.mybatis.config.parser.IMybatisConfigurationParser; import com.yang.mybatis.config.parser.IMybatisMapperParser; import com.yang.mybatis.mapper.MapperProxyFactory; import java.util.List; public class MybatisSqlSessionFactoryBuilder { private IMybatisConfigurationParser mybatisConfigurationParser; private IMybatisMapperParser mybatisMapperParser; private String configPath; public MybatisSqlSessionFactory buildSqlSessionFactory() { if (configPath == null || configPath.isEmpty()) { throw new RuntimeException("配置文件路径不合法==========" ); } if (this.mybatisMapperParser == null || this.mybatisConfigurationParser == null ) { throw new RuntimeException("缺少解析器=======" ); } MybatisConfiguration mybatisConfiguration = mybatisConfigurationParser.parser(configPath); List<String> mapperPaths = mybatisConfiguration.getMapperPaths(); for (String mapperPath : mapperPaths) { MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration = this.mybatisMapperParser.parseMapper(mapperPath); List<MybatisSqlStatement> mybatisSqlStatements = mybatisMapperXmlConfiguration.getMybatisSqlStatements(); for (MybatisSqlStatement mybatisSqlStatement : mybatisSqlStatements) { String mapperMethod = mybatisSqlStatement.getNamespace() + "." + mybatisSqlStatement.getId(); mybatisConfiguration.putMapperMethod2MybatisSqlStatement(mapperMethod, mybatisSqlStatement); } mybatisConfiguration.putMapperXmlConfiguration(mybatisMapperXmlConfiguration.getMapperName(), mybatisMapperXmlConfiguration); } MapperProxyFactory mapperProxyFactory = new MapperProxyFactory(mybatisConfiguration); return new DefaultMybatisSqlSessionFactory(mapperProxyFactory); } public MybatisSqlSessionFactoryBuilder setConfigPath(String configPath) { this.configPath = configPath; return this; } public MybatisSqlSessionFactoryBuilder setMybatisConfigurationParser(IMybatisConfigurationParser iMybatisConfigurationParser) { this.mybatisConfigurationParser = iMybatisConfigurationParser; return this; } public MybatisSqlSessionFactoryBuilder setMybatisMapperParser(IMybatisMapperParser iMybatisMapperParser) { this.mybatisMapperParser = iMybatisMapperParser; return this; } }
之前对于结果的解析,我们都是在DefaultMybatisSqlSession类中进行的,但是现在我们发现,结果的类型逐渐变得多样性了,如果都放在DefaultMybatisSqlSession类中,会使这个类十分庞大,因此,我们将解析结果的职责, 提取到IMybatisResultParser类中,首先定义该接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.yang.mybatis.execute; import com.yang.mybatis.execute.request.MybatisResultParserRequest; import java.sql.SQLException; public interface IMybatisResultParser { final static int ONE_COLUMNE = 0 ; final static int RESULT_TYPE = 1 ; final static int RESULT_MAP = 2 ; <T> T parseResult(MybatisResultParserRequest mybatisResultParserRequest) throws SQLException;}
MybatisResultParserRequest:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package com.yang.mybatis.execute.request; import com.yang.mybatis.config.MybatisMapperXmlConfiguration; import com.yang.mybatis.config.MybatisSqlStatement; import java.io.Serializable; import java.sql.ResultSet; public class MybatisResultParserRequest implements Serializable { private ResultSet resultSet; private MybatisSqlStatement mybatisSqlStatement; private MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration; public MybatisResultParserRequest() { } public ResultSet getResultSet() { return resultSet; } public void setResultSet(ResultSet resultSet) { this.resultSet = resultSet; } public MybatisSqlStatement getMybatisSqlStatement() { return mybatisSqlStatement; } public void setMybatisSqlStatement(MybatisSqlStatement mybatisSqlStatement) { this.mybatisSqlStatement = mybatisSqlStatement; } public MybatisMapperXmlConfiguration getMybatisMapperXmlConfiguration() { return mybatisMapperXmlConfiguration; } public void setMybatisMapperXmlConfiguration(MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration) { this.mybatisMapperXmlConfiguration = mybatisMapperXmlConfiguration; } }
然后定义其具体实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 package com.yang.mybatis.execute; import com.google.common.base.CaseFormat; import com.yang.mybatis.config.MybatisMapperXmlConfiguration; import com.yang.mybatis.config.MybatisResultMap; import com.yang.mybatis.config.MybatisResultMapProperty; import com.yang.mybatis.config.MybatisSqlStatement; import com.yang.mybatis.execute.request.MybatisResultParserRequest; import org.apache.commons.lang3.StringUtils; import java.lang.reflect.Field; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; public class DefaultMybatisResultParser implements IMybatisResultParser { @Override public <T> T parseResult(MybatisResultParserRequest mybatisResultParserRequest) throws SQLException { ResultSet resultSet = mybatisResultParserRequest.getResultSet(); if (resultSet == null ) { return null ; } MybatisSqlStatement mybatisSqlStatement = mybatisResultParserRequest.getMybatisSqlStatement(); MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration = mybatisResultParserRequest.getMybatisMapperXmlConfiguration(); int resultTypeCode = parseResultTypeCode(mybatisSqlStatement); switch (resultTypeCode) { case ONE_COLUMNE: return parseResultOfOneColumn(resultSet, mybatisSqlStatement); case RESULT_TYPE: return parseResultOfResultType(resultSet, mybatisSqlStatement); } return parseResultOfResultMap(resultSet, mybatisSqlStatement, mybatisMapperXmlConfiguration); } private <T> T parseResultOfResultMap(ResultSet resultSet, MybatisSqlStatement mybatisSqlStatement, MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration) throws SQLException { String resultMap = mybatisSqlStatement.getResultMap(); MybatisResultMap mybatisResultMap = mybatisMapperXmlConfiguration.getMybatisResultMap(resultMap); String classType = mybatisResultMap.getType(); Map<String, String> fieldName2ColumnNameMap = mybatisResultMap.getProperties() .stream() .collect(Collectors.toMap(MybatisResultMapProperty: : getProperty, MybatisResultMapProperty: : getColumn)); return parseResultOfClassAndFields(resultSet, classType, fieldName2ColumnNameMap); } private <T> T parseResultOfResultType(ResultSet resultSet, MybatisSqlStatement mybatisSqlStatement) throws SQLException { String resultType = mybatisSqlStatement.getResultType(); try { Class<?> aClass = Class.forName(resultType); Field[ ] fields = aClass.getDeclaredFields(); Map<String, String> fieldName2ColumnNameMap = new HashMap<>(); for (Field field : fields) { String columnName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName()); fieldName2ColumnNameMap.put(field.getName(), columnName); } return parseResultOfClassAndFields(resultSet, resultType, fieldName2ColumnNameMap); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } private <T> T parseResultOfClassAndFields(ResultSet resultSet, String classType, Map<String, String> fieldName2ColumnNameMap ) throws SQLException { try { Class<?> aClass = Class.forName(classType); Field[ ] fields = aClass.getDeclaredFields(); Map<String, Field> fieldName2FieldMap = new HashMap<>(); for (Field field : fields) { fieldName2FieldMap.put(field.getName(), field); } Object result = aClass.newInstance(); while (resultSet.next()) { for (Map.Entry<String, String> entry : fieldName2ColumnNameMap.entrySet()) { String fieldName = entry.getKey(); String columnName = entry.getValue(); Object columnValue = resultSet.getObject(columnName); Field field = fieldName2FieldMap.get(fieldName); field.setAccessible(true ); field.set(result, columnValue); } } return (T)result; } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } private <T> T parseResultOfOneColumn(ResultSet resultSet, MybatisSqlStatement mybatisSqlStatement) throws SQLException { return (T) resultSet.getObject(1 ); } private int parseResultTypeCode(MybatisSqlStatement mybatisSqlStatement) { String resultType = mybatisSqlStatement.getResultType(); String resultMap = mybatisSqlStatement.getResultMap(); if (StringUtils.isEmpty(resultType) && StringUtils.isEmpty(resultMap)) { return ONE_COLUMNE; } if (StringUtils.isNotEmpty(resultType) && StringUtils.isNotEmpty(resultMap)) { throw new RuntimeException("resultType和resultMap不能同时存在" ); } if (StringUtils.isNotEmpty(resultType)) { return RESULT_TYPE; } return RESULT_MAP; } }
最后,我们修改DefaultMybatisSqlSession类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 package com.yang.mybatis.session; import com.yang.mybatis.config.MybatisConfiguration; import com.yang.mybatis.config.MybatisEnvironment; import com.yang.mybatis.config.MybatisMapperXmlConfiguration; import com.yang.mybatis.config.MybatisSqlStatement; import com.yang.mybatis.execute.DefaultMybatisResultParser; import com.yang.mybatis.execute.IMybatisResultParser; import com.yang.mybatis.execute.request.MybatisResultParserRequest; import com.yang.mybatis.proxy.MapperProxyFactory; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Map; public class DefaultMybatisSqlSession implements IMybatisSqlSession { private MapperProxyFactory mapperProxyFactory; private MybatisConfiguration mybatisConfiguration; private IMybatisResultParser iMybatisResultParser = new DefaultMybatisResultParser(); public DefaultMybatisSqlSession(MapperProxyFactory mapperProxyFactory) { this.mapperProxyFactory = mapperProxyFactory; this.mybatisConfiguration = mapperProxyFactory.getMybatisConfiguration(); } @Override public <T> T execute(String method, Object parameter) { Map<String, MybatisSqlStatement> mapperMethod2SqlStatementsMap = mapperProxyFactory.getMybatisConfiguration().getMapperMethod2SqlStatementsMap(); MybatisSqlStatement mybatisSqlStatement = mapperMethod2SqlStatementsMap.get(method); MybatisEnvironment defaultMybatisEnvironment = this.mybatisConfiguration.getDefaultMybatisEnvironment(); return new TransactionInvoke<T>() { @Override public T execute(Connection connection) throws SQLException { String rawSql = mybatisSqlStatement.getSql(); List<String> parameterNameList = new ArrayList<>(); String sql = extractRawSql(rawSql, parameterNameList); Object[ ] parameters = (Object[ ] ) parameter; PreparedStatement preparedStatement = connection.prepareStatement(sql); if (parameterNameList.size() != parameters.length) { throw new RuntimeException("SQL语句参数个数不匹配====" ); } int index = 1 ; for (Object o : parameters) { preparedStatement.setObject(index ++, o); } ResultSet resultSet = preparedStatement.executeQuery(); String mapperName = mybatisSqlStatement.getNamespace(); MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration = mybatisConfiguration.getMybatisMapperXmlConfiguration(mapperName); MybatisResultParserRequest mybatisResultParserRequest = new MybatisResultParserRequest(); mybatisResultParserRequest.setResultSet(resultSet); mybatisResultParserRequest.setMybatisSqlStatement(mybatisSqlStatement); mybatisResultParserRequest.setMybatisMapperXmlConfiguration(mybatisMapperXmlConfiguration); T result = iMybatisResultParser.parseResult(mybatisResultParserRequest); resultSet.close(); return result; } } .invoke(defaultMybatisEnvironment.getMybatisDataSource()); } private String extractRawSql(String rawSql, List<String> parameterNameList) { StringBuilder sqlBuilder = new StringBuilder(); int start = 0 ; int end = -1 ; while ((end = rawSql.indexOf("#" , start)) != -1 ) { sqlBuilder.append(rawSql.substring(start, end - 1 )) .append(" ? " ); int parameterStart = end + 2 ; int parameterEnd = rawSql.indexOf("}" , parameterStart); parameterNameList.add(rawSql.substring(parameterStart, parameterEnd)); start = parameterEnd + 1 ; } sqlBuilder.append(rawSql.substring(start)); return sqlBuilder.toString(); } @Override public <T> T getMapper(Class<T> type) { return (T) mapperProxyFactory.newInstance(type, this); } }
添加测试代码,进行测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void main(String[ ] args) { String configPath = "mybatis-config.xml" ; IMybatisSqlSessionFactory mybatisSqlSessionFactory = new MybatisSqlSessionFactoryBuilder() .setMybatisConfigurationParser(new XmlMybatisConfigurationParser()) .setMybatisMapperParser(new XmlMybatisMapperParser()) .setConfigPath(configPath) .buildSqlSessionFactory(); IMybatisSqlSession mybatisSqlSession = mybatisSqlSessionFactory.openSession(); IUserMapper userMapper = mybatisSqlSession.getMapper(IUserMapper.class); IdUserNameVO idUserNameVO = userMapper.queryIdUserNameVOById(1 ); System.out.println(idUserNameVO.getId()); System.out.println(idUserNameVO.getUserName()); }
测试结果如下: