本章重点讲述了MyBatis映射器,对数据层进行的操作,建议本篇文章和Spring Boot的持久层相互比较来看会更加收获颇多Spring Boot持久层技术
本文需要使用到MVC第一讲的模板格式与配置情况,全部代码已经放在此博客中,Spring MVC1
目录
MyBatis映射器
映射器主要元素
元素 | 描述 |
---|---|
select | 映射查询 |
insert | 映射插入 |
update | 映射更新 |
delete | 映射删除 |
sql | 可以被其他语句引用的可重用语句块 |
resultMap | 描述层数据库结果集加载对象 |
cache | 缓存配置 |
cache-ref | 缓存配置的引用 |
注意MyBatis的配置我是在AyUserMapper.xml进行配置,所定位的Dao层如下图查看,通过mapper标签对dao接口进行一对一的映射,这是相对复杂的xml配置,后面会讲到注解的方式
同时在applicationContext.xml进行扫描:
select 元素
可以从数据库读取数据,组装数据给业务人员。比如在AyUserMapper.xml使用select元素,根据id查找(相关文件与建表命令在Spring MVC1列出)
AyUserMapper.xml
<select id="findById" parameterType="String" resultType="com.ay.model.AyUser">
select * from ay_user where id = #{id}
</select>
这个语句就是findById,接收一个String类型的参数,并返回一个User.#{id}就是预处理语句,类似于:
String findById = "select * from ay_user where id = ?";
PreparedStatement ps = conn.prepareStatement(findById);
ps.setString(1,id);
在接口AyUserDao,AyUserService写下此方法:
AyUser findById(String id);
在AyUserServiceImpl:
public AyUser findById(String id) {
return ayUserDao.findById(id);
}
下面进入到控制层进行测试:AyUserController:
@GetMapping("/findById")
public String findById(Model model) {
AyUser m = null;
m=ayUserService.findById("1");
System.out.println(m.getName()+"AAA");
return "hello";
}
运行,浏览器输入80/user/findById,可以在控制台看到相应的信息
xml中的select id注意要和DAO接口的方法名要一致,否则不一致会抛出异常。
resultType:从语句返回期望类型的类完全限定类名;
下面再来看几个例子:
<select id="countByName" parameterType="String" resultType="int">
select count(*) from ay_user
</select>
对于的AyUserDao如下:
int countByName(String name);
insert 元素
用来映射DML语句,MyBatis会在执行插入后返回一个整数,来表示操作后的记录数。
下面的属性如下:
属性 | 描述 |
---|---|
useGeneratedKeys | 令MyBatis使用JDBC来获取数据库的主键 |
keyColumn | 指明第几列为主键,不与keyProperty公用 |
keyProperty | 指明哪个列作为主键 |
下面来看几个例子: |
<insert id="insertUserTest" parameterType="com.ay.model.AyUser">
insert into ay_user(id,name,password) values(#{id},#{name},#{password});
</insert>
//或者使用主键自增的方法
<insert id="insertUserTest1" useGeneratedKeys="true" keyProperty="id" parameterType="com.ay.model.AyUser">
insert into ay_user(name,password) values(#{name},#{password});
</insert>
对应的AyUserDao,AyUserService接口如下:
int insertUserTest(AyUser ayUser);
实现的AyUserServiceImpl:
public int insertUserTest(AyUser ayUser){
return ayUserDao.insertUserTest(ayUser);
}
在控制层进行测试:
@GetMapping("/insertUserTest")
public String insertUserTest(Model model) {
AyUser m =new AyUser();
m.setId(9);m.setName("ccc");m.setPassword("1234");
int t=ayUserService.insertUserTest(m);
System.out.println(t);
return "hello";
}
运行80/user/insertUserTest,此时查看数据库信息:
update元素
用来映射DML语句,主要用来更新数据,同样也会返回一个整数来表示更新的记录数,和select属性差不多。
<update id="updateUser" parameterType="com.ay.model.AyUser">
update ay_user set name = #{name},password = #{password} where id = #{id}
</update>
在AyUserDao,AyUserService接口实现:
int updateUser(AyUser ayUser);
在AyUserServiceImpl中实现:
public int updateUser(AyUser ayUser) {
return ayUserDao.updateUser(ayUser);
}
在控制层实现:
@GetMapping("/updateUser")
public String updateUser(Model model) {
AyUser m = new AyUser();
m.setId(1);m.setName("jacinx");m.setPassword("88888");
int t = ayUserService.updateUser(m);
return "hello";
}
运行80/user/updateUser,查看数据库信息:
delete元素
用来映射删除,和select差不多,这里就稍微写一下xml:
//根据Id删除记录
<delete id ="delete" parameterType = "int">
delete from ay_user where id = #{id}
</delete>
sql元素
sql元素可以用来定义可重用的SQL代码,可以被静态参数化,可以对字段进行封装:
<sql id="userField">
a.id as "id",
a.name as "name",
a.password as "password"
</sql>
<!-- 获取所有用户-->
<select id="findAll" resultType="com.ay.model.AyUser">
select
<include refid="userField"/>
from ay_user as a
</select>
当然方便地使用include元素的refid属性进行引用,还可以使用定制使用sql元素,具体如下:
<sql id="userField">
//注意使用$而不是使用#
${prefix}.id as "id",
${prefix}.name as "name",
${prefix}.password as "password"
</sql>
#与$区别:
#{}将传入的数据当成字符串,会自动加一个双引号,例如:
order by #{id}
//如果id传入为11,那么解析成
order by "11"
${}将数据直接显示在sql中
order by ${id}
order by 11
#方式可以防止sql注入,$无法防止!
动态SQL
在项目开发中,经常根据不同的条件拼接SQL语句,而MyBatis提供了对SQL语句动态的组装能力。采用功能强大的基于OGNL表达式来完成动态SQL。
属性 | 描述 |
---|---|
if | 单条件分支 |
choose、when、otherwise | 多条件判断语句,相当于case when |
trim、where、set | 用来处理SQL拼接问题,辅助元素 |
foreach | 循环语句 |
if元素
用来判断语句,比如按照name查询,name可填可不填,如果没填那么就不要使用它来作为查询条件。
if标签常常与test属性联合使用且是必选属性。下面的代码来判断name 或者password是否为空,如果不为空那么就拼凑sql进行查询,如果为空则忽略:
<resultMap id="userMap" type="com.ay.model.AyUser">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="password" column="password"/>
</resultMap>
<select id="findByNameAndPassword" parameterType="String" resultMap="userMap">
select <include refid="userField"></include>
from ay_user a where 1 = 1
<if test="name != null and name !=''">
and name = #{name}
</if>
<if test="password !=null and password !=''">
and password = #{password}
</if>
</select>
在AyUserDao,AyUserService接口方法:
List<AyUser> findByNameAndPassword(@Param("name") String name,@Param("password") String password);
在AyUserServiceImpl实现接口的方法:
public List<AyUser> findByNameAndPassword(@Param("name") String name, @Param("password") String password) {
return ayUserDao.findByNameAndPassword(name,password);
}
在控制层实现:
@GetMapping("/findByNameAndPass")
public String findByNameAndPass(Model model) {
List<AyUser> a = null;
a = ayUserService.findByNameAndPassword("ccc","1234");
for(int i=0;i<a.size();i++) {
System.out.println(a.get(i).getId()+" "+a.get(i).getName());
}
return "hello";
}
运行80/user/findByNameAndPass,可以看到控制台打印出相应的信息:
关于其他的元素例如foreach等就不再赘述了。
Mybatis注解配置
常用注解
除了前面的xml的配置方法,可以使用基于注解的配置方式!
@Select注解与xml的select相对应,@Results注解与resultMap标签相对应,当使用注解的时候就不用在xml配置了:
在AyUserDao层接口方法:
@Select("select * from ay_user ")
List<AyUser> findAllUser();
其他AyUserService不变
List<AyUser> findAllUser();
在AyUserServiceImpl中:
public List<AyUser> findAllUser() {
return ayUserDao.findAllUser();
}
在控制层实现测试:
@GetMapping("/findAlUser")
public String findAlUser(Model model) {
List<AyUser> a = null;
a = ayUserService.findAllUser();
for(int i=0;i<a.size();i++) {
System.out.println(a.get(i).getName());
System.out.println();
}
return "hello";
}
此时运行就能看到输出了。
其他的例如@Insert,@Update,@Delete类似
@Insert("insert into ay_user(name,password) values(#{name},#{password})")
当映射器需要多个参数,@Param注解可以被应用于映射器方法参数给每个参数取名字。
List<AyUser> findByNameAndPassword(@Param("name") String name,@Param("password") String password);
MyBatis关联映射
在实际的项目中,需要遵循数据库设计范式的要求,对现实中的业务模块进行拆分,封装到不同的数据表中,表和表之间存在一对多或者多对多的对应关系。进而,对数据库的增删改查的主体从单表就变成了多表。
这里就不再赘述了。
思考:DAO与Service层
不知大家发现没有,在我的上述代码中,DAO与Service层的代码都是一样的,当然这是由于我是表单模式下的。所以这就引发我对DAO与Service层的思考:
这里引用一下zhihu的问题:java为什么要分为service层,dao层,controller层?
Service类封装业务流程(或者说是界面上的业务流程),DAO类封装对持久层的访问,DTO类封装业务实体对象。为什么要用Service接口和DAO接口?其实就是为了解耦,解耦说的意思是你更改某一层代码,不会影响我其他层代码。
Service + DAO,即DAO中只做CRUD及类似的简单操作(称之为功能点,不包含业务逻辑),Service中通过调用一个或多个DAO中的功能点来组合成为业务逻辑.Service的数量应该由功能模块来决定。在这种模型中业务逻辑是放在Service中的,事务的边界也应该在Service中控制.
以上,希望对你有所帮助。