持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 23 天,点击查看活动详情
上期课程中,按照前面的设计思路的分析,接下来要完成的任务是创建 SqlSession 接口以及实现类 DefaultSqlSession。当然对于 SqlSession 接口和实现类的创建,在上期课程中已经完成了创建,但是还没有在接口和实现类中去定义与数据库进行交互的增删改查方法。
在本期课程中,要完成的任务就是把这些方法定义编写。在自定义持久层框架中,只完成 selectList 和 selectOne 这两个方法编写和实现,不会把所有的增删改查的方式编写和实现。这两个方法已经涵盖了很多种情况。比如,selectOne 已经包含了既要封装返回结果以及要设置参数。
不管是要完成做修改还删除,只要能把 selectOne 实现出来,再去完成修改和删除。那相信你也都是能够实现的。这里只完成 selectList 和 selectOne 编写和实现即可。
在 SqlSession 接口中定义方法
在这个 SqlSession 接口中定义两个方法,一个是查询所有的 selectList() 方法,另一个是根据条件查询的 selectOne() 方法。
查询所有的 selectList() 的返回值是 List 集合,同时 List 集合的泛型如何定义。例如,对用户模块进行查询所有操作,是不是把所有查询处理的结果封装成一个用户(User)对象,再把这些用户(User)对象封装成 List 集合进行返回。
如果我们实行用户用户模块,List 集合中的范型应该用户(User)。但是还有可能是在完成商品模块。如果是商品模块,我们需要把查询商品表中的每一条记录封装成一个商品(Product )的实体来进行返回。
意味着在自定义持久层框架的时候,SqlSession 接口中的 selectList 的泛型不能写成具体的某个实体,因此,把这个 selectList() 方法的返回值的集合泛型定义 E,只要能保证同一类型即可。
那么,这个 selectList() 方法需不需要参数,答案是肯定需要的。其中,一个参数是字符串类型的 statementId。使用端再去调用 SqlSession 接口中的 selectList() 的方法时,需要去传递一个 statementId。这个 statementId 对应的就是映射配置文件中 namedspace.id,因为 namespace.id 是 SQL 语句的唯一标识。只有传递过来 statementId 才知道最终要执行的是哪一条 SQL 语句,才能够根据传递过来这个 statementId 在 Configuration 实体中,根据 Map 集合中 K 值取到提前封装好的 MapperStatement。因为已经对 XML 文件进行了解析,把里面的 SQL 语句、参数类型、返回结果类型等信息全部都封装到了 MapperStatement。想取出来对应的 MapperStatement,就必须在使用端传递过来一个 statementId。
除了需要这个 statementId 参数以外,还需不需要其他的参数,如果查询所有,只有 statementId 即可,但是如果使用模糊查询,并且返回结果也是多条记录,因此这里还需要一个参数,是一个 Object 类型的可变参数。
同样,在定义 selectOne() 方法和定义 selectList() 方法是一样的,返回值类型是某一个实体类型。
public interface SqlSession {
// 查询所有
public <E> List<E> selectList(String statementId, Object... params);
//根据条件查询
public <T> T selectOne(String statementId, Object... params);
}
在 SqlSession 接口定义完 selectList() 方法和 selectOne() 方法之后,在使用端的测试类中,通过 slqSession. 可以看到这两个方法,如下图所示。
完成 SqlSession 实现类中 selectOne 方法
在 selectList 和 selectOne 方法中,要完成的 JDBC 代码编写,但是这样实现不太好,我们在想把 JDBC 再去封装到下一层,也就是前面思路分析中创建这个 Executor 接口以及它的实现类 SimpleExecuto。把 JDBC 的代码编写到这个接口和实现类里面的 query() 方法中。
而在 selectList() 和 selectOne() 方法想要与数据库进行交互,执行底层的 JDBC 代码,只需要在 selectList() 和 selectOne() 方法中去调用这个 SimpleExecutor 里面的 query() 方法即可,并把它需要的一些参数进行传递。
在实现selectList() 和 selectOne() 方法这两个方法是,我们主要实现的是 selectList() 方法。selectOne 这个方法执行的时候,调用 selectList() 方法即可。在调用 selectList() 方法时,拿到对应结果集合,并对获取到的结果集合进行判断。如果结果集合的长度为 1,返回当前结果集合的记录,如果结果集合的长度不为 1,抛出运行时异常。
@Override
public <T> T selectOne(String statementId, Object... params) {
List<Object> objects = selectList(statementId, params);
if (objects.size()==1){
return (T)objects.get(0);
}else {
throw new RuntimeException("查询结果为空或者返回结果过多");
}
}
创建 Executor 接口以及实现类 SimpleExecutor
在这个 selectList() 方法中,将要完成 SimpleExecutor 类中的 query() 方法调用。目前,SimpleExecutor 类和 Executor 接口还没有,基于开闭原则,先把 Executor 接口和它的实现类 SimpleExecutor 来创建,具体实现在后期课程在来完成。
创建Executor 接口并在该接口中定义 query() 方法。具体代码如下:
public interface Executor {
public <E> List<E> query(Configuration configuration, MapperStatement mapperStatement,Object... params);
}
SimpleExecutor 类实现了 Executor 接口,并且重写了 Executor 接口中的 query() 方法。具体代码如下:
public class SimpleExecutor implements Executor{
@Override
public <E> List<E> query(Configuration configuration, MapperStatement mapperStatement, Object... params) {
return null;
}
}
完成 SqlSession 实现类中 selectList 方法
把 Executor 接口和实现类 SimpleExecutor 创建之后,那么,我们就可以在 selectList() 方法完成 query() 调用。
@Override
public <E> List<E> selectList(String statementId, Object... params) {
SimpleExecutor simpleExecutor=new SimpleExecutor();
MapperStatement mapperStatement=configuration.getMapperStatementMap().get(statementId);
List<Object> list = simpleExecutor.query(configuration, mapperStatement, params);
return (List<E>) list;
}
在调用 query() 方法时,需要传递三个参数:
第一个参数是 configuration。这个参数是在生产 defaultSqlSession 时,进行向下传递 configuration。
第二个参数是 mapperStatement。这个 mapperStatement 在封装在哪儿呢?封装在 Configuration 中的 Map 集合中,通过 statementId 获取 Map 集合中 mapperStatement。
第三个参数是 params 可变参。
最后将 List 集合进行返回。
本期课程完成 SqlSession 接口方法定义以及完成 DefaultSqlSession 类方法的实现。在下期课程中,将要完成 SimpleExecutor 类中的 query() 方法。
小结
本期课程,在 SqlSession 接口中定义两个方法,一个是 selectList() 和 selectOne() 方法,并在 SqlSession 的实现类 DefaultSqlSession 类中实现了中两个方法。同时基于开闭原则创建了 Executor 接口和实现类 SimpleExecutor 类,并在接口中定义 query() 方法。