项目:简易Mybatis

目录

一、新建项目

二、新建模块

三、回顾JDBC

四、准备环境

五、使用dom4j解析xml文件

 六、开始,编写Mapper解析API

1、自定义Resources类

2、定义Configuration类

3、定义MappedStatement类

4、定义XmlMapperBuilder类

5、更新一下UserMapper.xml和UserMapper接口

6、测试解析结果

七、解析config.xml文件

1、导入连接池依赖

2、定义XmlConfigBuilder类

3、测试

八、获取SQLSession

1、编写SqlSession接口

2、编写SqlSessionFactory接口

3、编写DefaultSqlSession类实现接口

4、编写DefaultSqlSessionFactory类实现接口

5、编写SqlSessionFactoryBuilder类

6、测试SqlSession是否创建成功

九、编写执行器

1、编写BoundSql类用于封装sql语句

2、编写Executor接口,定义执行器方法

3、编写SimpleExecutor用于实现Executor接口

4、更新DefaultSqlSession类,实现selectList和selectOne方法

5、编写测试类进行测试

十、增加新增接口

(1)更改代码

(2)测试

十一、增加更新接口

(1)更新代码

(2)测试

十二、增加删除接口

(1)修改mapper文件及更改DefaultSqlSession类

(2)测试

十三、优化代码为按标签类型分

(1)bug点

(2)在MappedStatement类中增加sqlType属性

(3)封装时将标签名一同进行封装

(4)修改DefaultSqlSession类中的代码

(5)修改SimpleExecutor类中的方法

(6)测试

十四、优化代码(使框架支持Integer作为参数)

(1)修改SimpleExecutor类的代码

(2)测试

十五、总结


一、新建项目

1、新建空项目

2、命名

3、删除父项目src


二、新建模块

1、右键父项目,新建模块

2、命名


三、回顾JDBC

1、存在问题

  1. 数据库连接创建、释放频繁造成系统资源浪费,从而影响性能。
  2. sql语句存在硬编码,造成代码不易维护。
  3. 使用preparedStatement向占有位符号传参数存在硬编码问题。
  4. 对结果解析存在硬编码(查询列名),sql变化导致解析代码变化。

2、问题解决

  1. 数据库频繁创建连接以及释放资源:连接池
  2. sql语句及参数存在硬编码:配置文件XxxMapper.xml
  3. 手动解析封装返回结果集:反射、内省

1、创建测试类

package test;import org.junit.Test;public class TestJdbc {@Testpublic 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 {@Testpublic 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");//获取preparedStatementpreparedStatement = connection.prepareStatement("select * from handwrite.mybatistable");//执行sql返回ResultSetresultSet = 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

    @Testpublic 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");//获取preparedStatementpreparedStatement = connection.prepareStatement("INSERT INTO handwrite.mybatistable values (null,?)");preparedStatement.setObject(1,"李四");//执行sqlint 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&amp;characterEncoding=utf-8&amp;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;@Beforepublic void init(){//解析xmlString 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);}@Testpublic 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 {@Testpublic 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.UserString 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 {@Testpublic 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);//根节点 configurationElement 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 {@Testpublic 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 {//获取sqlSessionSqlSession 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;}@Overridepublic <T> List<T> selectList(String statementId, Object... params) throws Exception {return null;}@Overridepublic <T> T selectOne(String statementId, Object... params) throws Exception {return null;}@Overridepublic <T> T insert(String statementId, Object... params) throws Exception {return null;}@Overridepublic <T> T update(String statementId, Object... params) throws Exception {return null;}@Overridepublic <T> T delete(String statementId, Object... params) throws Exception {return null;}/*** 为Mapper层的接口JDK动态代理生成实现类* @param mapperClass 字节码* @return 接口的代理类对象* @param <T> 反向* @throws Exception*/@Overridepublic <T> T getMapper(Class<?> mapperClass) throws Exception {Object instance = Proxy.newProxyInstance(mapperClass.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {@Overridepublic 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;}@Overridepublic 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);//创建SqlSessionFactoryDefaultSqlSessionFactory 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{@Overridepublic <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.UserString 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);}//执行SqLString 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对象完成数据库的查询

    @Overridepublic <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;}@Overridepublic <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;@Beforepublic void init(){try {//解析xmlString 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);}}@Testpublic void testList(){List<User> list = userMapper.list();for (User user : list) {System.out.println(user);}}@Testpublic void testFindById(){User user = userMapper.findById(1);System.out.println(user);}
}

(1)执行后发现报错

(2)debug进行错误定位

(3)发现是因为程序想在Integer类中找到id属性时出错,于是更改findById的传入值为User

(4)重新测试

    @Testpublic void testFindById(){User u = new User();u.setId(1);User user = userMapper.findById(u);System.out.println(user);}


十、增加新增接口

(1)更改代码

    @Overridepublic <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)测试

    @Testpublic void testInsert(){User user = new User();user.setUsername("qingti");int insert = userMapper.insert(user);System.out.println(insert>0?"新增成功":"新增失败");}


十一、增加更新接口

(1)更新代码

    @Overridepublic <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)测试

    @Testpublic void testUpdate(){User user = new User(6,"kunkun");int update = userMapper.update(user);System.out.println(update>0?"修改成功":"修改失败");}


十二、增加删除接口

(1)修改mapper文件及更改DefaultSqlSession类

    @Overridepublic <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)测试

    @Testpublic 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类中的代码

    @Overridepublic <T> T getMapper(Class<?> mapperClass) throws Exception {Object instance = Proxy.newProxyInstance(mapperClass.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {@Overridepublic 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]);}}

(2)测试


十五、总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1475273.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Redis基础教程(十六):Redis Stream

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

读书记录《SQL从小白到大牛》01

读书记录《SQL从小白到大牛》01 接地气的书名&#xff0c;内容应当值得一读。 第一篇 SQL基础 01 一些基础概念 SQL是结构化查询语言&#xff08;Structured Query Language&#xff09;&#xff0c;是一套用来输入、更改和查看关系数据库内容的命令。数据库发展经历三个阶…

SMA 内孔 弯头——KH-SMA-K513-G

品  牌&#xff1a; kinghelm(金航标) 厂家型号&#xff1a; KH-SMA-K513-G 封装&#xff1a; 插件 商品毛重&#xff1a; 2.86克(g) 包装方式&#xff1a; 袋装

使用Mybatis批量插入大量数据的实践

前言 在项目开发过程中&#xff0c;我们经常会有批量插入的需求&#xff0c;例如&#xff1a;定时统计任务 但是受限于MySQL中 max_allowed_packet 参数限制&#xff0c;5.7版本默认值为4M&#xff0c;这显然不太符合我们的需求&#xff0c;当然我们也可以通过修改此值来适应…

【Unity几种数据存储之间的区别】PlayerPrefs、Json、XML、二进制、SQLite数据存储之间的优缺点以及如何选择

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 专栏交流&#x1f9e7;&…

Meta关于深度学习推荐系统的Scaling Law的研究

作者 | 番茄爱鸡蛋 整理 | NewBeeNLP https://zhuanlan.zhihu.com/p/688913185 大家好&#xff0c;这里是 NewBeeNLP。今天看看 Meta 关于深度学习推荐系统 Scaling Law 的研究。 零、论文信息 论文题目&#xff1a;Wukong: Towards a Scaling Law for Large-Scale Recommend…

更好的预测方法:使用前后控制图

我已经写了很多关于阶段控制图的文章&#xff0c;因为我认为它们是一个非常好的可视化工具。它们有许多用途而且很容易创建。除了有助于分析改进或变更前后的流程之外&#xff0c;它们还是更准确预测或预报的重要第一步。 不同的预测方式或用不同的方法预测 有很多不同的方法…

硅纪元视角 | Speak火了!3个月收入翻倍,OpenAI为何频频下注?

在数字化浪潮的推动下&#xff0c;人工智能&#xff08;AI&#xff09;正成为塑造未来的关键力量。硅纪元视角栏目紧跟AI科技的最新发展&#xff0c;捕捉行业动态&#xff1b;提供深入的新闻解读&#xff0c;助您洞悉技术背后的逻辑&#xff1b;汇聚行业专家的见解&#xff0c;…

信息技术课堂上如何有效防止学生玩游戏?

防止学生在信息技术课堂上玩游戏需要综合运用教育策略和技术手段。以下是一些有效的措施&#xff0c;可以用来阻止或减少学生在课堂上玩游戏的行为&#xff1a; 1. 明确课堂规则 在课程开始之初&#xff0c;向学生清楚地说明课堂纪律&#xff0c;强调不得在上课时间玩游戏。 制…

【十八】【QT开发应用】标签页QTabWidget的常见用法

#include "widget.h" // 包含自定义的widget头文件 #include <QHBoxLayout> // 包含QHBoxLayout头文件&#xff0c;用于水平布局 #include <QTabWidget> // 包含QTabWidget头文件&#xff0c;用于创建标签页控件 #include <QDebug> // 包含QDebug头…

医院人员管理项目01_下午,css

文章目录 层叠样式表在html文件中引入css样式表&#xff1a;2种方法如何设置样式&#xff1a;3种css选择器继承权重 层叠样式表 引入html网页中的方式&#xff0c;共3种。 行内样式&#xff08;内联样式&#xff09;&#xff1a;直接在html中设置 内部样式&#xff1a;css代…

学诚教育在线管理系统-计算机毕业设计源码98076

目 录 摘要 1 绪论 1.1 选题背景与意义 1.2开发现状 1.3论文结构与章节安排 2 开发环境及相关技术介绍 2.1 MySQL数据库 2.2 Tomcat服务器 2.3 Java语言 2.4 Spring Cloud框架介绍 3 教育在线管理系统系统分析 3.1 可行性分析 3.1.1 技术可行性分析 3.1.2 经济可…

【Proteus仿真】基于Stm32的八路抢答器~

【Proteus仿真】基于Stm32的八路抢答器~ 文档资料在购买后即可获得&#xff08;如有问题可通过微信公号或b站私信联系我&#xff09; 资料包括&#xff1a; 1. Proteus仿真源文件2. keil源代码功能描述: 1. 抢答时间设置显示2. 选手得分用时显示3. 选手数据查询/清楚4.抢答…

Java | Leetcode Java题解之第213题打家劫舍II

题目&#xff1a; 题解&#xff1a; class Solution {public int rob(int[] nums) {int length nums.length;if (length 1) {return nums[0];} else if (length 2) {return Math.max(nums[0], nums[1]);}return Math.max(robRange(nums, 0, length - 2), robRange(nums, 1,…

App UI性能测试 - PerfDog使用全教程

App 性能测试指标: 响应、内存、CPU、FPS、GPU渲染、耗电、耗流等。 PerfDog的性能数据更加全面,所以下面以PerfDog来介绍安装使用流程及测试数据的获取与分析。 官网: PerfDog | 全平台性能测试分析专家 第一步,先访问官网进行注册, 注册好账号后,点击下载PerfDog,下…

【算法笔记自学】第 7 章 提高篇(1)——数据结构专题(1)

7.1栈的应用 #include <iostream> #include <string> #include <stack> using namespace std;int main() {int n, x;string action;cin >> n;stack<int> s;for (int i 0; i < n; i) {cin >> action;if (action "push") {ci…

昇思25天学习打卡营第19天|Pix2Pix实现图像转换

1. 学习内容复盘 Pix2Pix概述 Pix2Pix是基于条件生成对抗网络&#xff08;cGAN, Condition Generative Adversarial Networks &#xff09;实现的一种深度学习图像转换模型&#xff0c;该模型是由Phillip Isola等作者在2017年CVPR上提出的&#xff0c;可以实现语义/标签到真实…

生活商城app微信小程序模板源码

红色的卷皮折扣电商app小程序&#xff0c;综合生活购物商城app小程序前端模板下载。包含&#xff1a;首页、分类、购物车、列表、商品详情、个人中心、优惠券、全部订单、生活超市专题等等。一套很全通用的商城app小程序模板。 生活商城app微信小程序模板源码

YOLOv8标签可视化

这一章主要是为了可视化YOLO标签设置的,为什么要进行可视化呢,因为很多时候我们标注好数据还需要进行转换成YOLO格式,这期间如果出现转换的错误,而我们没有去检查标签的话,有可能导致训练无法得到很好的一个结果,所以需要我们对YOLO标签进行可视化来检查标签的情况。 这部…

《梦醒蝶飞:释放Excel函数与公式的力量》9.5 IRR函数

9.5 IRR函数 IRR函数是Excel中用于计算内部收益率&#xff08;Internal Rate of Return, IRR&#xff09;的函数。内部收益率是评估投资项目盈利性的重要指标&#xff0c;它表示使投资项目的净现值&#xff08;NPV&#xff09;为零的折现率。 9.5.1 函数简介 IRR函数通过一系…