引言 在上一章中,我们成功实现了数据库的连接,以及单个字段的查询、resultType映射查询、resultMap映射查询。在本章,我们将讲解关于增加、修改和删除操作。
insert操作 首先,我们修改IUserMapper类,添加insertUser接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.yang.mybatis.test; public interface IUserMapper { String queryUserName(Integer id); Integer queryUserAge(Integer id); User queryUserById(Integer id); IdUserNameVO queryIdUserNameVOById(Integer id); void insertUser(User user);}
修改UserMapper.xml,加上insertUser相关的xml块
1 2 3 4 <insert id="insertUser" parameterType="com.yang.mybatis.test.User" > insert into user( user_name, password, age, create_time) values( #{ userName} , #{ password} , #{ age} , #{ createTime} ) </insert>
我们注意到,对于每一个接口方法,其对应的xml块,类型有select,insert,update和delete这几种类型,此外,还有一个parameterType参数,因此,我们需要再次修改MybatisSqlStatement类,加上operateType和parameterType参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MybatisSqlStatement implements Serializable { private String namespace; private String id; private String sql; private String resultType; private String resultMap; private String operateType; private String parameterType; ...省略getter 和setter}
接着修改XmlMybatisMapperParser的parseStatement方法,在解析mapper.xml的时候,设置对应的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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 parameterType = element.attributeValue("parameterType" ); String sql = element.getText().trim(); MybatisSqlStatement mybatisSqlStatement = new MybatisSqlStatement(); mybatisSqlStatement.setNamespace(namespace); mybatisSqlStatement.setId(id); mybatisSqlStatement.setSql(sql); mybatisSqlStatement.setResultType(resultType); mybatisSqlStatement.setResultMap(resultMap); mybatisSqlStatement.setParameterType(parameterType); mybatisSqlStatement.setOperateType(element.getName().toLowerCase()); mybatisMapperXmlConfiguration.addMybatisSqlStatement(mybatisSqlStatement); } }
定义IMybatisPreparedStatementBuilder接口,用于接收请求参数并设置参数值到preparedStatement
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.yang.mybatis.execute; import com.yang.mybatis.execute.request.MybatisPreparedStatementBuilderRequest; import java.sql.PreparedStatement; import java.sql.SQLException; public interface IMybatisPreparedStatementBuilder { PreparedStatement buildPreparedStatement(MybatisPreparedStatementBuilderRequest mybatisPreparedStatementBuilderRequest) throws SQLException;}
其中,MybatisPreparedStatementBuilderRequest的定义如下:
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.execute.request; import com.yang.mybatis.config.MybatisSqlStatement; import java.io.Serializable; import java.sql.Connection; public class MybatisPreparedStatementBuilderRequest implements Serializable { private Connection connection; private Object[ ] parameters; private MybatisSqlStatement mybatisSqlStatement; private String operateType; public Connection getConnection() { return connection; } public void setConnection(Connection connection) { this.connection = connection; } public Object[ ] getParameters() { return parameters; } public void setParameters(Object[ ] parameters) { this.parameters = parameters; } public MybatisSqlStatement getMybatisSqlStatement() { return mybatisSqlStatement; } public void setMybatisSqlStatement(MybatisSqlStatement mybatisSqlStatement) { this.mybatisSqlStatement = mybatisSqlStatement; } public String getOperateType() { return operateType; } public void setOperateType(String operateType) { this.operateType = operateType; } }
该接口的默认实现类为:
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 package com.yang.mybatis.execute; import com.yang.mybatis.config.MybatisSqlStatement; import com.yang.mybatis.execute.request.MybatisPreparedStatementBuilderRequest; import org.apache.commons.lang3.StringUtils; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class DefaultMybatisPreparedStatementBuilder implements IMybatisPreparedStatementBuilder { @Override public PreparedStatement buildPreparedStatement(MybatisPreparedStatementBuilderRequest mybatisPreparedStatementBuilderRequest) throws SQLException { Connection connection = mybatisPreparedStatementBuilderRequest.getConnection(); Object[ ] parameters = mybatisPreparedStatementBuilderRequest.getParameters(); MybatisSqlStatement mybatisSqlStatement = mybatisPreparedStatementBuilderRequest.getMybatisSqlStatement(); String rawSql = mybatisSqlStatement.getSql(); List<String> parameterNameList = new ArrayList<>(); String sql = extractRawSql(rawSql, parameterNameList); PreparedStatement preparedStatement = connection.prepareStatement(sql); String parameterType = mybatisSqlStatement.getParameterType(); if (StringUtils.isEmpty(parameterType)) { if (parameterNameList.size() != parameters.length) { throw new RuntimeException("SQL语句参数个数不匹配====" ); } int index = 1 ; for (Object o : parameters) { preparedStatement.setObject(index ++, o); } return preparedStatement; } try { Object parameter = parameters[ 0 ] ; Class<?> aClass = Class.forName(parameterType); Field[ ] fields = aClass.getDeclaredFields(); Map<String, Field> filedName2FieldMap = new HashMap<>(); for (Field field : fields) { filedName2FieldMap.put(field.getName(), field); } List<Object> parameterValueList = new ArrayList<>(); for (String parameterName : parameterNameList) { Field field = filedName2FieldMap.get(parameterName); if (field == null ) { throw new RuntimeException("SQL参数不存在" ); } field.setAccessible(true ); Object value = field.get(parameter); parameterValueList.add(value); } int index = 1 ; for (Object o : parameterValueList) { preparedStatement.setObject(index++, o); } return preparedStatement; } catch (ClassNotFoundException | IllegalAccessException e) { e.printStackTrace(); 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(); } }
然后,我们修改DefaultMybatisSqlSession类,在execute方法中我们先使用IMybatisPreparedStatementBuilder来构建PreparedStatement,然后执行相关操作,最后再通过IMybatisResultParser来对结果进行解析。
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 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.DefaultMybatisPreparedStatementBuilder; import com.yang.mybatis.execute.DefaultMybatisResultParser; import com.yang.mybatis.execute.IMybatisPreparedStatementBuilder; import com.yang.mybatis.execute.IMybatisResultParser; import com.yang.mybatis.execute.request.MybatisPreparedStatementBuilderRequest; 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.Map; public class DefaultMybatisSqlSession implements IMybatisSqlSession { private MapperProxyFactory mapperProxyFactory; private MybatisConfiguration mybatisConfiguration; private IMybatisResultParser iMybatisResultParser; private IMybatisPreparedStatementBuilder iMybatisPreparedStatementBuilder; public DefaultMybatisSqlSession(MapperProxyFactory mapperProxyFactory, IMybatisPreparedStatementBuilder iMybatisPreparedStatementBuilder, IMybatisResultParser iMybatisResultParser) { this.mapperProxyFactory = mapperProxyFactory; this.mybatisConfiguration = mapperProxyFactory.getMybatisConfiguration(); this.iMybatisPreparedStatementBuilder = iMybatisPreparedStatementBuilder; this.iMybatisResultParser = iMybatisResultParser; } @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 { Object[ ] parameters = (Object[ ] ) parameter; String operateType = mybatisSqlStatement.getOperateType(); MybatisPreparedStatementBuilderRequest mybatisPreparedStatementBuilderRequest = new MybatisPreparedStatementBuilderRequest(); mybatisPreparedStatementBuilderRequest.setConnection(connection); mybatisPreparedStatementBuilderRequest.setMybatisSqlStatement(mybatisSqlStatement); mybatisPreparedStatementBuilderRequest.setParameters(parameters); mybatisPreparedStatementBuilderRequest.setOperateType(operateType); PreparedStatement preparedStatement = iMybatisPreparedStatementBuilder.buildPreparedStatement(mybatisPreparedStatementBuilderRequest); ResultSet resultSet = null ; if ("select" .equals(operateType)) { resultSet = preparedStatement.executeQuery(); } else { preparedStatement.execute(); } String mapperName = mybatisSqlStatement.getNamespace(); MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration = mybatisConfiguration.getMybatisMapperXmlConfiguration(mapperName); MybatisResultParserRequest mybatisResultParserRequest = new MybatisResultParserRequest(); mybatisResultParserRequest.setResultSet(resultSet); mybatisResultParserRequest.setMybatisSqlStatement(mybatisSqlStatement); mybatisResultParserRequest.setMybatisMapperXmlConfiguration(mybatisMapperXmlConfiguration); mybatisResultParserRequest.setOperateType(operateType); T result = iMybatisResultParser.parseResult(mybatisResultParserRequest); if (resultSet != null ) { resultSet.close(); } return result; } } .invoke(defaultMybatisEnvironment.getMybatisDataSource()); } @Override public <T> T getMapper(Class<T> type) { return (T) mapperProxyFactory.newInstance(type, this); } }
这里还需要修改DefaultMybatisSqlSessionFactory类,将IMybatisResultParser和IMybatisPreparedStatementBuilder的构建职责迁移于此,从而避免每一个SqlSession都需要重复创建这些类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.yang.mybatis.session; import com.yang.mybatis.execute.DefaultMybatisPreparedStatementBuilder; import com.yang.mybatis.execute.DefaultMybatisResultParser; import com.yang.mybatis.execute.IMybatisPreparedStatementBuilder; import com.yang.mybatis.execute.IMybatisResultParser; import com.yang.mybatis.proxy.MapperProxyFactory; public class DefaultMybatisSqlSessionFactory implements IMybatisSqlSessionFactory { private MapperProxyFactory mapperProxyFactory; private IMybatisPreparedStatementBuilder iMybatisPreparedStatementBuilder = new DefaultMybatisPreparedStatementBuilder(); private IMybatisResultParser iMybatisResultParser = new DefaultMybatisResultParser(); public DefaultMybatisSqlSessionFactory(MapperProxyFactory mapperProxyFactory) { this.mapperProxyFactory = mapperProxyFactory; } @Override public IMybatisSqlSession openSession() { return new DefaultMybatisSqlSession(mapperProxyFactory, iMybatisPreparedStatementBuilder, iMybatisResultParser); } }
添加测试代码,进行测试:
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 package com.yang.mybatis.test; import com.yang.mybatis.config.parser.XmlMybatisConfigurationParser; import com.yang.mybatis.config.parser.XmlMybatisMapperParser; import com.yang.mybatis.session.IMybatisSqlSession; import com.yang.mybatis.session.IMybatisSqlSessionFactory; import com.yang.mybatis.session.MybatisSqlSessionFactoryBuilder; import java.time.LocalDateTime; public class Main { 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 = new User(); user.setUserName("test" ); user.setPassword("test" ); user.setAge(4 ); user.setCreateTime(LocalDateTime.now()); userMapper.insertUser(user); } }
运行上述方法,再次查看数据库,结果如下:
update操作 首先,我们在IUserMapper接口上,添加一个updateUserById方法
1 2 void updateUserById(User user);
然后修改UserMapper.xml,添加上updateUserById相关的xml块
1 2 3 4 5 6 7 <update id="updateUserById" parameterType="com.yang.mybatis.test.User" > update user set user_name = #{ userName} , password = #{ password} , age = #{ age} where id = #{ id} </update>
添加测试方法,进行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 String configPath = "mybatis-config.xml" ; MybatisSqlSessionFactory 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(4 ); user.setUserName("cxy1" ); user.setPassword("1234" ); user.setAge(10 ); userMapper.updateUserById(user);
运行方法后,查看数据库,结果如下:
delete操作 首先,修改IUserMapper,添加deleteUserById方法
1 2 void deleteUserById(Integer id);
然后在UserMapper.xml中添加上对应的xml块
1 2 3 4 <delete id="deleteUserById" parameterType="int" > delete from user where id = #{ id} </delete>
添加测试方法,进行测试:
1 2 3 4 5 6 7 8 9 10 11 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); userMapper.deleteUserById(5 ); }
结果如下:
可以看出,这里因为我们使用的是parameterType是int而不是java.lang.Integer,所以会报错,但是一般情况下,像int,float,long等小写,一般我们也会使用的,而且用的频率比java.lang.Integer的频率更大,那么,我们其实可以添加一个假名,将int这些小写的方式和对应的包装类关联起来。我们修改DefaultMybatisPreparedStatementBuilder,修改内容如下:
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 package com.yang.mybatis.execute; import com.yang.mybatis.config.MybatisSqlStatement; import com.yang.mybatis.execute.request.MybatisPreparedStatementBuilderRequest; import org.apache.commons.lang3.StringUtils; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class DefaultMybatisPreparedStatementBuilder implements IMybatisPreparedStatementBuilder { private Map<String, String> baseAliasMap = new HashMap<>(); { baseAliasMap.put("int" , Integer.class.getName()); baseAliasMap.put("float" , Float.class.getName()); baseAliasMap.put("double" , Double.class.getName()); baseAliasMap.put("long" , Long.class.getName()); baseAliasMap.put("byte" , Byte.class.getName()); baseAliasMap.put("short" , Short.class.getName()); baseAliasMap.put("string" , String.class.getName()); baseAliasMap.put("String" , String.class.getName()); baseAliasMap.put("char" , Character.class.getName()); } @Override public PreparedStatement buildPreparedStatement(MybatisPreparedStatementBuilderRequest mybatisPreparedStatementBuilderRequest) throws SQLException { Connection connection = mybatisPreparedStatementBuilderRequest.getConnection(); Object[ ] parameters = mybatisPreparedStatementBuilderRequest.getParameters(); MybatisSqlStatement mybatisSqlStatement = mybatisPreparedStatementBuilderRequest.getMybatisSqlStatement(); String rawSql = mybatisSqlStatement.getSql(); List<String> parameterNameList = new ArrayList<>(); String sql = extractRawSql(rawSql, parameterNameList); PreparedStatement preparedStatement = connection.prepareStatement(sql); String parameterType = mybatisSqlStatement.getParameterType(); if (StringUtils.isEmpty(parameterType)) { if (parameterNameList.size() != parameters.length) { throw new RuntimeException("SQL语句参数个数不匹配====" ); } int index = 1 ; for (Object o : parameters) { preparedStatement.setObject(index ++, o); } return preparedStatement; } try { Object parameter = parameters[ 0 ] ; if (baseAliasMap.containsKey(parameterType)) { if (parameters.length != 1 ) { throw new RuntimeException("SQL语句有误, 只能有一个parameterType参数" ); } preparedStatement.setObject(1 , parameter); return preparedStatement; } Class<?> aClass = Class.forName(parameterType); Field[ ] fields = aClass.getDeclaredFields(); Map<String, Field> filedName2FieldMap = new HashMap<>(); for (Field field : fields) { filedName2FieldMap.put(field.getName(), field); } List<Object> parameterValueList = new ArrayList<>(); for (String parameterName : parameterNameList) { Field field = filedName2FieldMap.get(parameterName); if (field == null ) { throw new RuntimeException("SQL参数不存在" ); } field.setAccessible(true ); Object value = field.get(parameter); parameterValueList.add(value); } int index = 1 ; for (Object o : parameterValueList) { preparedStatement.setObject(index++, o); } return preparedStatement; } catch (ClassNotFoundException | 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(); } }
再次运行测试方法,这次没有报错了,然后我们查看数据库,结果如下,说明删除成功了。