在前一章节,我们对自定义持久层框架有了一个基本的思路,接下来我们主要就是需要完成自定义持久层级框架的编写
首先我们先来编写使用端代码,新建一个maven项目,maven项目中pom.xml文件内容如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>study.lagou.com</groupId>
<artifactId>persistence-test</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>
在项目的resources文件夹下创建sqlMapConfig.xml配置文件
<configuration>
<dataSource>
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost/mybatis"></property>
<property name="username" value="root"></property>
<property name="password" value="111111"></property>
</dataSource>
</configuration>
接着创建mapper.xml配置文件,但是我们在创建之前,先思考一个问题,在我们的一个项目当中,是不是应该会有很多个模块,不可能只有一个模块,可能有用户、商品、订单等等模块,如果我们只有一个mapper.xml文件话,所有的SQL语句都要写到这一个mapper.xml文件当中,这样做好吗?如果所有的SQL语句都写到一个mapper.xml文件,对于我们的操作和编写都是非常麻烦的,而且不易于阅读,为了使我们的mapper.xml文件当中的内容不至于太长,容易阅读和维护,所以我们推荐按照模块分别编写mapper.xml文件
接下来我们就按照不同的模块来编写不同的映射配置文件,比如当前我们有一个用户模块和商品模块,那么我们则需要编写两个映射配置文件
一个是UserMapper.xml,这个映射配置文件只用来编写用户相关的SQL语句
另外一个是ProduceMapper.xml,这个一配置文件只用来编写商品相关的SQL语句
接下来我们先以UserMapper.xml的编写来一步步演示,具体编写mapper.xml文件时需要注意的问题
<mapper>
<!-- 查询所有的用户信息 -->
<select>
select * from user
</select>
<!-- 根据条件查询单个用户信息 -->
<select>
select * from user where id = ? and username = ?
</select>
</mapper>
简单的编写完以上的UserMapper.xml文件,我们可以看到在我们UserMapper.xml文件中有两条SQL语句,那么我们如何能够定位到映射配置文件中这两条SQL语句呢(具体调用时如何区分应该调用哪一条),如何区分是查询所有还是查询单个?我们无法区分,所以我们考虑给每一个SQL语句给一个标识属性(id),有了以下改进版本
<mapper>
<!-- 查询所有的用户信息 -->
<select id="selectList">
select * from user
</select>
<!-- 根据条件查询单个用户信息 -->
<select id="selectOne">
select * from user where id = ? and username = ?
</select>
</mapper>
如此改进完成之后,我们则可以通过selectList、和selectOne定位到具体我们需要的是哪一条SQL语句了,接着思考,我们改进完成之后还有没有其它问题呢?
如果是项目当中只有这一个模块,是没有问题的,但是如果项目当中还存在其它模块,其它模块也是这么编写的呢?我们以ProduceMapper.xml来举例说明
<mapper>
<!-- 查询所有的商品信息 -->
<select id="selectList">
select * from produce
</select>
<!-- 根据条件查询单个商品信息 -->
<select id="selectOne">
select * from produce where id = ? and username = ?
</select>
</mapper>
ProduceMapper.xml文件中也是这么写的,取的标识名也是selectList和selectOne,只是查询的表名不一样,现在通过selectList和selectOne这两个标识还能定位到唯一一条SQL语句吗?因为selectList和selectOne在项目当中存在多个了,我们不能区分开来,所以我需要再加一层标识加以识别处理==>命名空间namespace
<mapper namespace="user">
<!-- 查询所有的用户信息 -->
<select id="selectList">
select * from user
</select>
<!-- 根据条件查询单个用户信息 -->
<select id="selectOne">
select * from user where id = ? and username = ?
</select>
</mapper>
<mapper namespace="produce">
<!-- 查询所有的商品信息 -->
<select id="selectList">
select * from produce
</select>
<!-- 根据条件查询单个商品信息 -->
<select id="selectOne">
select * from produce where id = ? and produce_name = ?
</select>
</mapper>
现在我们可以约定,SQL的唯一标识,可以由namespace+"."+id来组成,这样就能唯一标识出唯一一条SQL语句了,由namespace.id组合成的唯一标识,我们将它称之为statementId
接着往下分析,现在我们能够获取到唯一一条SQL语句进行执行,但是执行完成后,返回的结果集还是需要我们手动去解析放到到具体的对象属性当中,这个手动封装返回结果集的过程较为繁琐,我需要借助于反射、内省等机制,完成返回结果集与对象属性的自动解析和封装,那么我们必须获取到对应对象的全路径才能够做操作,所以我们还需要给有返回结果集的查询语句,添加resultType属性,将返回对象的全路径放置进去,我们才能通过反射、内省等技术进行操作处理
<mapper namespace="user">
<!-- 查询所有的用户信息 -->
<select id="selectList" resultType="study.lagou.com.persistence.test.pojo.User">
select * from user
</select>
<!-- 根据条件查询单个用户信息 -->
<select id="selectOne" resultType="study.lagou.com.persistence.test.pojo.User">
select * from user where id = ? and username = ?
</select>
</mapper>
至此,对selectList方法,我们能够完成基本的操作,但是我们接着看selectOne方法,在执行selectOne方法的时候,我们需要传入参数,才能够构建查询的SQL语句,所以在selectOne方法上我们还得添加paramterType属性,而针对多个参数的传递,还是得基于面向对象的思想进行传递,所以接下来我们再对selectOne方法进行完善
<mapper namespace="user">
<!-- 查询所有的用户信息 -->
<select id="selectList" resultType="study.lagou.com.persistence.test.pojo.User">
select * from user
</select>
<!-- 根据条件查询单个用户信息 -->
<select id="selectOne" resultType="study.lagou.com.persistence.test.pojo.User"
paramterType="study.lagou.com.persistence.test.pojo.User">
select * from user where id = ? and username = ?
</select>
</mapper>
将参数对象通过paramterType属性给到自定义持久层框架,持久层框架需要做的就是通过反射,取到参数对象的属性值,取到对应的属性值之后,我们要给对应的占位符进行赋值,但是我们怎么定位到将哪个属性值赋值到哪一个占位符上面呢?显然,我们通过以“?”做为占位符的方法,无法完成对应,所以我们需要对占位符进行改进,改进成#{属性}的方式来进行处理,注意:#{}当中的属性,需要与paramterType属性对象中的属性一一对应才行,不能随便给!
<mapper namespace="user">
<!-- 查询所有的用户信息 -->
<select id="selectList" resultType="study.lagou.com.persistence.test.pojo.User">
select * from user
</select>
<!-- 根据条件查询单个用户信息 -->
<select id="selectOne" resultType="study.lagou.com.persistence.test.pojo.User"
paramterType="study.lagou.com.persistence.test.pojo.User">
select * from user where id = #{id} and username = #{username}
</select>
</mapper>
我们在占位符中给的是id和username两个属性,那么paramterType对应对象中一定存在这两个属性值
package study.lagou.com.persistence.test.pojo;
/**
* @Description: 功能描述
* @Author houjh
* @Email: happyxiaohou@gmail.com
* @Date: 2021-1-27 22:13
*/
public class User {
/**
* 主键信息
*/
private Integer id;
/**
* 用户名称
*/
private String username;
/**
* 用户密码
*/
private String password;
/**
* 用户昵称
*/
private String nickname;
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 String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
至此,我们编写映射配置文件算是基本上完成了,但是上一节当中,我们在分析自定义持久层框架思路的时候,分析出自定义持久层框架的第一步就是加载配置文件,我们没有必要通过getResourceAsStream(String path)方法对配置文件加载两次,而是直接在sqlMapConfig.xml存放一份映射配置文件的全路径,后面通过解析sqlMapConfig.xml文件就可以获取到mapper.xml映射配置文件的全路径,对mapper文件进行操作,所以我们再对sqlMapConfig.xml配置文件进行完善
<configuration>
<dataSource>
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost/mybatis"></property>
<property name="username" value="root"></property>
<property name="password" value="111111"></property>
</dataSource>
<mapper resource="UserMapper.xml"></mapper>
</configuration>
至此,我们使用端代码编写完成