目录
5、更新一下UserMapper.xml和UserMapper接口
4、编写DefaultSqlSessionFactory类实现接口
3、编写SimpleExecutor用于实现Executor接口
4、更新DefaultSqlSession类,实现selectList和selectOne方法
(1)修改mapper文件及更改DefaultSqlSession类
(2)在MappedStatement类中增加sqlType属性
一、新建项目
1、新建空项目
2、命名
3、删除父项目src
二、新建模块
1、右键父项目,新建模块
2、命名
三、回顾JDBC
1、存在问题
- 数据库连接创建、释放频繁造成系统资源浪费,从而影响性能。
- sql语句存在硬编码,造成代码不易维护。
- 使用preparedStatement向占有位符号传参数存在硬编码问题。
- 对结果解析存在硬编码(查询列名),sql变化导致解析代码变化。
2、问题解决
- 数据库频繁创建连接以及释放资源:连接池
- sql语句及参数存在硬编码:配置文件XxxMapper.xml
- 手动解析封装返回结果集:反射、内省
1、创建测试类
package test; import org.junit.Test; public class TestJdbc { @Test public void testJdbc(){ } }
2、创建测试数据库及表
3、创建实体类
package com.qingti.pojo; import lombok.Data; @Data @AllArgsConstructor @NoArgsConstructor public class User { private Integer id; private String username; }
4、导入mysql依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
5、编写查询jdbc
package test;
import com.qingti.pojo.User;
import org.junit.Test;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class TestJdbc {
@Test
public void testJdbc(){
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
List<User> users = new ArrayList<User>();
try {
//加载JDBC驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接对象
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/handwrite?characterEncoding=utf-8&serverTimezone=Asia/Shanghai", "root", "123456");
//获取preparedStatement
preparedStatement = connection.prepareStatement("select * from handwrite.mybatistable");
//执行sql返回ResultSet
resultSet = preparedStatement.executeQuery();
//遍历数据,存入集合
while (resultSet.next()){
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
User user = new User(id,username);
users.add(user);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
//关闭连接
try {
if(resultSet!=null){
resultSet.close();
}
if (preparedStatement!=null){
preparedStatement.close();
}
if (connection!=null){
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
}
}
System.out.println("查询到的数据是:"+users);
}
}
6、运行测试
7、编写新增jdbc
@Test
public void testInsert(){
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//加载JDBC驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接对象
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/handwrite?characterEncoding=utf-8&serverTimezone=Asia/Shanghai", "root", "123456");
//获取preparedStatement
preparedStatement = connection.prepareStatement("INSERT INTO handwrite.mybatistable values (null,?)");
preparedStatement.setObject(1,"李四");
//执行sql
int count = preparedStatement.executeUpdate();
System.out.println(count>0?"新增成功":"新增失败");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
//关闭连接
try {
if(resultSet!=null){
resultSet.close();
}
if (preparedStatement!=null){
preparedStatement.close();
}
if (connection!=null){
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
}
}
}
8、运行测试
四、准备环境
1、编写UserMapper
public interface UserMapper { List<User> list(); }
2、编写resource/mapper
<mapper namespace="com.qingti.mapper.UserMapper"> <!-- 查询--> <select id="list" resultType="com.qingti.pojo.User"> select * from mybatistable </select> </mapper>
3、编写mybatis-config
<?xml version="1.0" encoding="UTF-8" ?> <configuration> <!--数据库配置信息--> <dataSource> <!--数据库的驱动地址--> <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/> <!--连接字符串--> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/handwrite?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> <!--存放mapper.xml的全路径--> <mapper resource="mapper/UserMapper.xml"/> </configuration>
4、编写测试代码
public class TestMybatis { UserMapper userMapper; @Before public void init(){ //解析xml String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); //框架底层使用JDK动态代理给接口生成实现类对象 userMapper = sqlSession.getMapper(UserDao.class); } @Test public void testList(){ List<User> list = userMapper.list(); for (User user : list) { System.out.println(user); } } }
五、使用dom4j解析xml文件
1、导入dom4j依赖
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.1</version>
</dependency>
2、在test目录创建测试xml
<?xml version="1.0" encoding="UTF-8" ?> <books> <book id="1"> <name>1</name> <id>1</id> </book> <book id="2"> <name>2</name> <id>2</id> </book> <book id="3"> <name>3</name> <id>3</id> </book> </books>
3、创建测试类
package test; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.junit.Test; import java.util.List; public class TestBook { @Test public void test(){ try { //创建一个解析器 SAXReader saxReader = new SAXReader(); //获取一个文档对象 Document document = saxReader.read("D:\\selfstudy\\MybatisHandWrite\\hand-write-ssm\\hand-write-mybatis\\src\\test\\resources\\book.xml"); //获取xml文件的根节点 Element rootElement = document.getRootElement(); System.out.println("根节点的名字是"+rootElement.getName()); //获取子节点的集合 List<Element> elements = rootElement.elements(); System.out.println("子节点的个数为:"+elements.size()); for (Element element : elements) { Attribute id = element.attribute("id"); String value = id.getValue(); System.out.println("id的值为:"+value); //book节点下的子节点集合 List<Element> elements1 = element.elements(); for (Element element1 : elements1) { //标签名 String tagName = element1.getName(); //标签内的内容 String text = element1.getText(); System.out.println(tagName+"="+text); } System.out.println("------------------------------"); } } catch (DocumentException e) { throw new RuntimeException(e); } finally { } } }
4、运行测试
六、开始,编写Mapper解析API
1、自定义Resources类
Resources类的作用是获取一个类加载器,根据配置文件的路径,将配置文件加载成字节输入流存储在内存中。
创建Resources类
@Data public class Resources { /** * 根据路径将配置文件加载为字节流的形式,存储在内存中 * @param path 配置文件的位置 * @return 返回的字节流 */ public static InputStream getResourceAsStream(String path) { //加载类路径下的配置文件,以字节流的形式返回 InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path); return resourceAsStream; } }
2、定义Configuration类
对sql语句进行封装
/** * 对sql语句进行封装 */ @Data public class Configuration { /** * 数据源 */ private DataSource dataSource; /** * 封装的mapper.xml文件中的sql语句,因为mapper中不止一条sql语句 */ Map<String,MappedStatement> mappedStatementMap = new ConcurrentHashMap<>(); }
3、定义MappedStatement类
MappedStatement类作用是封装UserMapper.xml文件解析之后的SQL语句信息,在底层框架可以使用Dom4j进行解析
/** * 对sql语句进行解析 */ @Data @AllArgsConstructor @NoArgsConstructor public class MappedStatement { //id标识 private String id; //sql语句返回值 private String resultType; //参数类型 private String parameterType; //sql语句 private String sql; }
4、定义XmlMapperBuilder类
使用dom4j解析Mapper.xml配置文件
/** * 使用dom4j解析Mapper.xml配置文件 */ public class XmlMapperBuilder { /** * 配置数据封装对象 */ private Configuration configuration; public XmlMapperBuilder(Configuration configuration) { this.configuration = configuration; } /** * 传入配置文件的字节流,解析配置文件,得到配置文件的封装 * @param intputStream */ public void parse(InputStream intputStream) throws DocumentException { SAXReader saxReader = new SAXReader(); //获取文档对象 Document document = saxReader.read(intputStream); //获取根节点 Element rootElement = document.getRootElement(); //获取根节点的属性 Attribute namespace = rootElement.attribute("namespace"); //com.qingti.pojo.User String namespaceValue = namespace.getValue(); //xpath解析,解析xml配置文件,获取所有查询相关的节点 List selectNodes = rootElement.selectNodes("//select"); //xpath解析,解析xml配置文件,获取所有增加相关的节点 List insertNodes = rootElement.selectNodes("//insert"); //xpath解析,解析xml配置文件,获取所有修改相关的节点 List updateNodes = rootElement.selectNodes("//update"); //xpath解析,解析xml配置文件,获取所有删除相关的节点 List deleteNodes = rootElement.selectNodes("//delete"); List<Element> allNodes = new ArrayList<>(); allNodes.addAll(selectNodes); allNodes.addAll(insertNodes); allNodes.addAll(updateNodes); allNodes.addAll(deleteNodes); for (Element element : allNodes) { //获取每条sql的id值 String id = element.attributeValue("id"); //获取返回值 String resultType = element.attributeValue("resultType"); //获取参数类型 String parameterType = element.attributeValue("parameterType"); //获取每个mappr节点中的sql语句 String sqlText = element.getTextTrim(); //封装对象 MappedStatement mappedStatement = new MappedStatement(); mappedStatement.setId(id); mappedStatement.setResultType(resultType); mappedStatement.setParameterType(parameterType); mappedStatement.setSql(sqlText); String key = namespaceValue+"."+id; configuration.getMappedStatementMap().put(key,mappedStatement); } } }
5、更新一下UserMapper.xml和UserMapper接口
(1)更新xml中的sql语句
<mapper namespace="com.qingti.mapper.UserMapper"> <!-- 查询--> <select id="list" resultType="com.qingti.pojo.User"> select * from mybatistable </select> <!-- id查--> <select id="findById" resultType="com.qingti.pojo.User" parameterType="java.lang.Integer"> select * from mybatistable where id=#{id} </select> <!-- 新增--> <insert id="insert" resultType="java.lang.Integer" parameterType="com.qingti.pojo.User"> insert into mybatistable values (null,#{username}) </insert> <!-- 修改--> <update id="update" resultType="java.lang.Integer" parameterType="com.qingti.pojo.User"> update mybatistable set username=#{username} where id=#{id} </update> <!-- 删除--> <delete id="delete" resultType="java.lang.Integer" parameterType="java.lang.Integer"> delete from mybatistable where id=#{id} </delete> </mapper>
(2)更新方法接口
public interface UserMapper { List<User> list(); User findById(Integer id); Integer add(User user); Integer update(User user); Integer delete(Integer id); }
6、测试解析结果
public class TestMapper { @Test public void test(){ try { Configuration configuration = new Configuration(); InputStream resourceAsStream = Resources.getResourceAsStream("mapper/UserMapper.xml"); XmlMapperBuilder mapperBuilder = new XmlMapperBuilder(configuration); mapperBuilder.parse(resourceAsStream); System.out.println(configuration); } catch (DocumentException e) { throw new RuntimeException(e); } } }
七、解析config.xml文件
1、导入连接池依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
2、定义XmlConfigBuilder类
public class XmlConfigBuilder { private Configuration configuration; public XmlConfigBuilder(Configuration configuration) { this.configuration = configuration; } public Configuration parseMyBatisXConfig(InputStream inputStream) throws DocumentException { SAXReader saxReader = new SAXReader(); Document document = saxReader.read(inputStream); //根节点 configuration Element rootElement = document.getRootElement(); List<Element> property = rootElement.selectNodes("//property"); Properties properties = new Properties(); for (Element element : property) { String name = element.attributeValue("name"); String value = element.attributeValue("value"); properties.setProperty(name,value); } //初始化数据库连接池 DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(properties.getProperty("driverClass")); druidDataSource.setUrl(properties.getProperty("jdbcUrl")); druidDataSource.setUsername(properties.getProperty("username")); druidDataSource.setPassword(properties.getProperty("password")); //设置数据库数据源 configuration.setDataSource(druidDataSource); //Mybatis的核心配置文件,映射Mapper.xml文件 List<Element> list = rootElement.selectNodes("//mapper"); for (Element element : list) { String resource = element.attributeValue("resource"); InputStream resourceAsStream = Resources.getResourceAsStream(resource); XmlMapperBuilder xmlMapperBuilder = new XmlMapperBuilder(configuration); xmlMapperBuilder.parse(resourceAsStream); } return configuration; } }
3、测试
public class TestXml { @Test public void test() throws DocumentException { Configuration configuration = new Configuration(); XmlConfigBuilder xmlConfigBuilder = new XmlConfigBuilder(configuration); InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml"); xmlConfigBuilder.parseMyBatisXConfig(resourceAsStream); System.out.println("解析完毕!"); System.out.println(configuration); } }
现在我们可以得到xml文件中的数据了,于是我们完成了第一步,加载配置文件
八、获取SQLSession
1、编写SqlSession接口
SqlSession是提供与数据库交互的各种方法的接口
public interface SqlSession { /** *查询所有数据 * @param statementId sql语句唯一ID * @param params 查询sql语句所需参数,可变参数 * @param <T> */ <T> List<T> selectList(String statementId, Object... params)throws Exception; /** *按条件查询单个对象 * @param statementId sql语句唯一ID * @param params 查询sql语句所需参数,可变参数 * @param <T> */ <T> T selectOne(String statementId, Object... params)throws Exception; /** *新增 * @param statementId sql语句唯一ID * @param params 查询sql语句所需参数,可变参数 * @param <T> */ <T> T insert(String statementId, Object... params)throws Exception; /** *更新 * @param statementId sql语句唯一ID * @param params 查询sql语句所需参数,可变参数 * @param <T> */ <T> T update(String statementId, Object... params)throws Exception; /** *删除 * @param statementId sql语句唯一ID * @param params 查询sql语句所需参数,可变参数 * @param <T> */ <T> T delete(String statementId, Object... params)throws Exception; /** * 为Mapper层的接口JDK动态代理生成实现类 * @param mapperClass 字节码 * @return 接口的代理类对象 * @param <T> 反向 * @throws Exception */ <T> T getMapper(Class<?> mapperClass)throws Exception; }
2、编写SqlSessionFactory接口
SqlSessionFactory是MyBatis的关键对象,它是单个数据库映射关系经过编译后的内存镜像
public interface SqlSessionFactory { //获取sqlSession SqlSession openSession(); }
3、编写DefaultSqlSession类实现接口
(1)先创建一个Command类来区分sql语句
public enum CommandType { INSERT, UPDATE, DELETE }
(2)编写实现类
public class DefaultSqlSession implements SqlSession{ //封装的配置信息 private Configuration configuration; public DefaultSqlSession(Configuration configuration) { this.configuration = configuration; } @Override public <T> List<T> selectList(String statementId, Object... params) throws Exception { return null; } @Override public <T> T selectOne(String statementId, Object... params) throws Exception { return null; } @Override public <T> T insert(String statementId, Object... params) throws Exception { return null; } @Override public <T> T update(String statementId, Object... params) throws Exception { return null; } @Override public <T> T delete(String statementId, Object... params) throws Exception { return null; } /** * 为Mapper层的接口JDK动态代理生成实现类 * @param mapperClass 字节码 * @return 接口的代理类对象 * @param <T> 反向 * @throws Exception */ @Override public <T> T getMapper(Class<?> mapperClass) throws Exception { Object instance = Proxy.newProxyInstance(mapperClass.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //接口中的方法名 String methodName = method.getName(); //接口的全类名 String className = method.getDeclaringClass().getName(); //拼接Sql的唯一标识 String statementId = className + "." + methodName; //获取方法被调用后的返回值类型 Type genericReturnType = method.getGenericReturnType(); if(methodName.contains(CommandType.INSERT.toString())){ return insert(statementId,args); } else if (methodName.contains(CommandType.DELETE.toString())) { return delete(statementId,args); } else if (methodName.contains(CommandType.UPDATE.toString())) { return update(statementId,args); } //判断是否进行了泛型类型的参数化(判断返回值类型是否是泛型) if (genericReturnType instanceof ParameterizedType){ List<Object> objects = selectList(statementId, args); return objects; }else{ return selectOne(statementId, args); } } }); return (T)instance; } }
4、编写DefaultSqlSessionFactory类实现接口
public class DefaultSqlSessionFactory implements SqlSessionFactory{ private Configuration configuration; public DefaultSqlSessionFactory(Configuration configuration){ this.configuration = configuration; } @Override public SqlSession openSession() { return new DefaultSqlSession(configuration); } }
5、编写SqlSessionFactoryBuilder类
该类用于生成SqlSessionFactory对象
public class SqlSessionFactoryBuilder { public SqlSessionFactory build(InputStream inputStream) throws DocumentException { //获取configuration对象 Configuration configuration = new Configuration(); XmlConfigBuilder xmlConfigBuilder = new XmlConfigBuilder(configuration); xmlConfigBuilder.parseMyBatisXConfig(inputStream); //创建SqlSessionFactory DefaultSqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(configuration); return sqlSessionFactory; } }
6、测试SqlSession是否创建成功
找到TestMysbatis并进行测试
九、编写执行器
1、编写BoundSql类用于封装sql语句
@Data @NoArgsConstructor @AllArgsConstructor public class BoundSql { //要执行的sql语句 private String sqlText; //执行sql的参数集合 private List<String> parameterMappingList = new ArrayList<>(); }
2、编写Executor接口,定义执行器方法
/** * sql语句执行器 */ public interface Executor { <T>List<T> query(Configuration configuration, MappedStatement mappedStatement,Object... params) throws SQLException, ClassNotFoundException, Exception; }
3、编写SimpleExecutor用于实现Executor接口
主要包含:
- Sql语句的转换
- Sql的执行
- 返回值的封装
/**
* sql语句的执行器
*/
public class SimpleExecutor implements Executor{
@Override
public <T> List<T> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
//1、获取数据库连接
Connection connection = configuration.getDataSource().getConnection();
//2、获取要执行的sql语句
String sql = mappedStatement.getSql();//拿到配置文件中的原始sql语句
//转换sql语句,把#{}转换为?
BoundSql boundSql = this.getBoundSql(sql);
//获取
PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
//设置参数:com.qingti.pojo.User
String parameterType = mappedStatement.getParameterType();
Class<?> classType = this.getClassType(parameterType);
//获取sql语句参数集合
List<String> parameterMappingList = boundSql.getParameterMappingList();
for (int i = 0;i < parameterMappingList.size();i++) {
String content = parameterMappingList.get(i);
//反射
Field declaredField = classType.getDeclaredField(content);
declaredField.setAccessible(true);
//取出参数
Object data = declaredField.get(params[0]);
preparedStatement.setObject(i+1,data);
}
//执行SqL
String id = mappedStatement.getId();
ResultSet resultSet = null;
if(id.contains(CommandType.DELETE.toString())||id.contains(CommandType.INSERT.toString())||id.contains(CommandType.UPDATE.toString())){
//增删改
Integer result = preparedStatement.executeUpdate();
ArrayList<Integer> resultList = new ArrayList<>();
resultList.add(result);
return (List<T>)resultList;
}else {
//查询
resultSet = preparedStatement.executeQuery();
}
//获取返回值的类型
String resultType = mappedStatement.getResultType();
Class<?> returnTypeClass = this.getClassType(resultType);
List<Object> objects = new ArrayList<>();
while (resultSet.next()){
//调无参构造方法生成对象
Object instance = returnTypeClass.newInstance();
ResultSetMetaData metaData = resultSet.getMetaData();
for (int i = 1; i <= metaData.getColumnCount(); i++) {
//字段名字
String columnName = metaData.getColumnName(i);
//获取值
Object value = resultSet.getObject(columnName);
//属性封装
//使用反射根据数据库表和实体类的属性和字段对应关系数据封装
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName,returnTypeClass);
Method writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(instance,value);
}
objects.add(instance);
}
return (List<T>)objects;
}
Map<Integer,Integer> map = new TreeMap<Integer,Integer>();
int findPosition = 0;
List<String> parameterMappings = new ArrayList<>();
/**
* 根据类的全名称获取Class
* @param parameterType
* @return
* @throws ClassNotFoundException
*/
public Class<?> getClassType(String parameterType) throws ClassNotFoundException {
if(parameterType!=null){
Class<?> aClass = Class.forName(parameterType);
return aClass;
}
return null;
}
/**
* 转换API
* 1、将#{}使用?代替
* 2、解析出#{}内的值进行存储
* @param sql
* @return
*/
private BoundSql getBoundSql(String sql){
//完成sql语句解析工作
this.parserSql(sql);
Set<Map.Entry<Integer,Integer>> entries = map.entrySet();
for (Map.Entry<Integer, Integer> entry : entries) {
Integer key = entry.getKey()+2;
Integer value = entry.getValue();
parameterMappings.add(sql.substring(key,value));
}
for (String s : parameterMappings) {
sql = sql.replace("#{"+s+"}","?");
}
BoundSql boundSql = new BoundSql(sql, parameterMappings);
return boundSql;
}
private void parserSql(String sql){
int openIndex = sql.indexOf("#{",findPosition);
if (openIndex != -1){
int endIndex = sql.indexOf("}",findPosition+1);
if(endIndex != -1){
map.put(openIndex,endIndex);
findPosition = endIndex+1;
parserSql(sql);//递归检查#{}
}else{
System.out.println("SQL语句中参数错误..");
}
}
}
}
4、更新DefaultSqlSession类,实现selectList和selectOne方法
使用Executor对象完成数据库的查询
@Override public <T> List<T> selectList(String statementId, Object... params) throws Exception { //将SimpleExecutorQuery方法完成查询 SimpleExecutor simpleExecutor = new SimpleExecutor(); MappedStatement mappedStatement = this.configuration.getMappedStatementMap().get(statementId); List<Object> list = simpleExecutor.query(configuration, mappedStatement, params); return (List<T>)list; } @Override public <T> T selectOne(String statementId, Object... params) throws Exception { List<Object> objects = this.selectList(statementId, params); if ((objects.size()==1)){ return (T) objects.get(0); }else if(objects.size()>1){ throw new RuntimeException("查询结果为空或者查询结果不唯一!"); }else{ throw new RuntimeException("查询结果为空!"); } }
5、编写测试类进行测试
public class TestMybatis {
UserMapper userMapper;
@Before
public void init(){
try {
//解析xml
String resource = "mybatis-config.xml";
//加载配置文件,并得到字节流
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建线程工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
System.out.println(sqlSession);
//框架底层使用JDK动态代理给接口生成实现类对象
userMapper = sqlSession.getMapper(UserMapper.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Test
public void testList(){
List<User> list = userMapper.list();
for (User user : list) {
System.out.println(user);
}
}
@Test
public void testFindById(){
User user = userMapper.findById(1);
System.out.println(user);
}
}
(1)执行后发现报错
(2)debug进行错误定位
(3)发现是因为程序想在Integer类中找到id属性时出错,于是更改findById的传入值为User
(4)重新测试
@Test public void testFindById(){ User u = new User(); u.setId(1); User user = userMapper.findById(u); System.out.println(user); }
十、增加新增接口
(1)更改代码
@Override public <T> T insert(String statementId, Object... params) throws Exception { SimpleExecutor simpleExecutor = new SimpleExecutor(); MappedStatement mappedStatement = this.configuration.getMappedStatementMap().get(statementId); List<Object> list = simpleExecutor.query(configuration, mappedStatement, params); if (list.size()>0){ return (T) list.get(0); }else { return (T) "0"; } }
(2)测试
@Test
public void testInsert(){
User user = new User();
user.setUsername("qingti");
int insert = userMapper.insert(user);
System.out.println(insert>0?"新增成功":"新增失败");
}
十一、增加更新接口
(1)更新代码
@Override public <T> T update(String statementId, Object... params) throws Exception { SimpleExecutor simpleExecutor = new SimpleExecutor(); MappedStatement mappedStatement = this.configuration.getMappedStatementMap().get(statementId); List<Object> list = simpleExecutor.query(configuration, mappedStatement, params); if (list.size()>0){ return (T) list.get(0); }else { return (T) "0"; } }
(2)测试
@Test
public void testUpdate(){
User user = new User(6,"kunkun");
int update = userMapper.update(user);
System.out.println(update>0?"修改成功":"修改失败");
}
十二、增加删除接口
(1)修改mapper文件及更改DefaultSqlSession类
@Override public <T> T delete(String statementId, Object... params) throws Exception { SimpleExecutor simpleExecutor = new SimpleExecutor(); MappedStatement mappedStatement = this.configuration.getMappedStatementMap().get(statementId); List<Object> list = simpleExecutor.query(configuration, mappedStatement, params); if (list.size()>0){ return (T) list.get(0); }else { return (T) "0"; } }
(2)测试
@Test public void testDelete(){ User user = new User(); user.setId(6); int delete = userMapper.delete(user); System.out.println(delete>0?"删除成功":"删除失败"); }
十三、优化代码为按标签类型分
(1)bug点
在DefaultSqlSession类的getMapper方法中
这种写法只是简单按照mapper中的方法名来判断用的是哪种sql语句(只能识别insert、update、delete、select)
(2)在MappedStatement类中增加sqlType属性
(3)封装时将标签名一同进行封装
(4)修改DefaultSqlSession类中的代码
@Override
public <T> T getMapper(Class<?> mapperClass) throws Exception {
Object instance = Proxy.newProxyInstance(mapperClass.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//接口中的方法名
String methodName = method.getName();
//接口的全类名
String className = method.getDeclaringClass().getName();
//拼接Sql的唯一标识
String statementId = className + "." + methodName;
//获取方法被调用后的返回值类型
Type genericReturnType = method.getGenericReturnType();
Map<String, MappedStatement> mappedStatementMap = configuration.getMappedStatementMap();
MappedStatement mappedStatement = mappedStatementMap.get(statementId);
if("insert".equals(mappedStatement.getSqlType())){
return insert(statementId,args);
} else if ("delete".equals(mappedStatement.getSqlType())) {
return delete(statementId,args);
} else if ("update".equals(mappedStatement.getSqlType())) {
return update(statementId,args);
}
//判断是否进行了泛型类型的参数化(判断返回值类型是否是泛型)
if (genericReturnType instanceof ParameterizedType){
List<Object> objects = selectList(statementId, args);
return objects;
}else{
return selectOne(statementId, args);
}
}
});
return (T)instance;
}
(5)修改SimpleExecutor类中的方法
(6)测试
此时,我们将insert的方法名改为add
修改测试方法为add
十四、优化代码(使框架支持Integer作为参数)
(1)修改SimpleExecutor类的代码
for (int i = 0;i < parameterMappingList.size();i++) {
String content = parameterMappingList.get(i);
//若不是Integer,则进行反射;否则直接传入参数
if (!"java.lang.Integer".equals(parameterType)){
//反射
Field declaredField = classType.getDeclaredField(content);
declaredField.setAccessible(true);
//取出参数
Object data = declaredField.get(params[0]);
preparedStatement.setObject(i+1,data);
}else {
preparedStatement.setObject(i+1,params[0]);
}
}