1.Mybatis 映射器
之前讲过通过Mybatis的核心配置文件,和定义一个书写sql的配置文件,然后通过SqlSession对象的方法执行sql语句,sql语句通过sql的xml文件的namespace属性和具体sql的id属性来定位到一个sql,今天会告诉大家通过Mybatis的映射器直接将dao层接口和sql的xml文件映射起来,然后执行dao层接口的方法完成CRUD。
1.1 Mybatis完成CRUD的操作步骤
- 创建数据库和建表
- 准备一个普通的项目
- 导入mybatis需要的jar包(核心包、依赖包、数据库驱动包)
- 准备好domain
- 准备好mybatis的核心配置文件
- 准备好dao层接口和书写sql的xml配置文件
- junit4单元测试
1.2 Mybatis的核心配置文件:MyBatis_config.xml
这个配置和之前的一样,唯一不同的地方就是在引入sql的xml文件的时候,需要注意自己文件的路径
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--环境配置,连接的数据库,这里使用的是MySQL-->
<environments default="development">
<environment id="development">
<!--指定事务管理的类型,这里简单使用Java的JDBC的提交和回滚设置-->
<transactionManager type="JDBC"></transactionManager>
<!--dataSource 指连接源配置,POOLED是JDBC连接对象的数据源连接池的实现-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</environment>
</environments>
<mappers>
<!--这是告诉Mybatis区哪找持久化类的映射文件,对于在resoures资源文件下的文件直接写文件名,如果在某包下,则要写明路径,如:com/mybatistest/config/User.xml-->
<mapper resource="cn/lqq/mapper/EmployeeMapper.xml"></mapper>
</mappers>
</configuration>
1.3 dao层接口和sql的xml文件
规范:
- mybatis中dao层的命名规范是mapper,mapper包其实就是dao层
- 接口的方法映射到对应的sql,接口的方法名必须和sql语句的id属性值对应上
- xml配置文件的namespace属性值必须和接口的完全限定名一致
mybatis写sql语句有两种方式:1.在xml文件中写 2.直接在接口方法上使用注解
下面注释的那一行代码就是在直接在方式上使用注解的方式来写sql语句
mapper包下的接口实现代码:
public interface EmployeeMapper {
//查询所有数据
// @Select("select * from employee")
List<Employee> findAll();
//查询一条数据
Employee findOne(Long id);
//保存数据
void save(Employee employee);
//修改数据
void update(Employee employee);
//删除数据
void delete(Long id);
}
对应的sql的xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--这里的命名空间namespace必须是到层接口的完全限定名,必须对应上,才会创建关系-->
<mapper namespace="cn.lqq.mapper.EmployeeMapper">
<!--查询所有数据-->
<select id="findAll" resultType="cn.lqq.domain.Employee">
select * from employee
</select>
<!--查询一条数据-->
<select id="findOne" parameterType="long" resultType="cn.lqq.domain.Employee">
select * from employee where id=#{id}
</select>
<!--保存数据-->
<insert id="save" parameterType="cn.lqq.domain.Employee">
insert into employee (name,age,sex) values(#{name},#{age},#{sex})
</insert>
<!--修改数据-->
<update id="update" parameterType="cn.lqq.domain.Employee">
update employee set name=#{name},age=#{age},sex=#{sex} where id=#{id}
</update>
<!--删除数据-->
<delete id="delete" parameterType="long">
delete from employee where id=#{id}
</delete>
</mapper>
注意: 这个xml文件要在Mybatis的核心配置文件中读取
1.4 单元测试
public class EmployeeTest {
@Test
public void testSave()throws Exception{
SqlSession session = MyBatiUtils.openSession();
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
for (int i = 0; i < 30; i++) {
Employee employee = new Employee();
employee.setName("李巧巧"+i);
employee.setAge(i);
employee.setSex(i%2==0);
mapper.save(employee);
}
session.commit();
}
@Test
public void testFindAll()throws Exception{
//创建Mapper对象,这个可以获取到EmployeeMapper接口的实现
EmployeeMapper mapper = MyBatiUtils.openSession().getMapper(EmployeeMapper.class);
//直接通过这个调用里面的方法
mapper.findAll().forEach(e-> System.out.println(e));
}
@Test
public void testFindOne()throws Exception{
EmployeeMapper mapper = MyBatiUtils.openSession().getMapper(EmployeeMapper.class);
Employee employee = mapper.findOne(1L);
System.out.println(employee);
}
@Test
public void testUpdate()throws Exception{
SqlSession session = MyBatiUtils.openSession();
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee employee = mapper.findOne(31L);
employee.setName("张三丰");
mapper.update(employee);
session.commit();
}
@Test
public void testDelete()throws Exception{
SqlSession session = MyBatiUtils.openSession();
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
mapper.delete( 31L);
session.commit();
}
}
2.Mybatis 高级查询
2.1 高级查询需要注意的点
- 高级查询的条件通过一个query对象传过来,要准备一个query对象
- 多个条件拼接的时候,直接使用where标签
- 模糊查询进行字符串的拼接使用:concat()函数进行拼接
- 特殊字符的处理:1.使用转义字符 2.使用CDATA区:<![CDATA[ 数据 ]]>
- 在if语句中有联接条件,使用and/or,而不是&&/ ||
- 在sql的xml中对于公共代码的提取:使用sql标签
2.2 query对象的准备
定义一个类作为高级查询的条件,这里只是为了做测试定义的,在实际开发中查询的条件是根据实际需求的
public class EmployeeQuery {
private String name;
private Integer minAge;
private Integer maxAge;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getMinAge() {
return minAge;
}
public void setMinAge(Integer minAge) {
this.minAge = minAge;
}
public Integer getMaxAge() {
return maxAge;
}
public void setMaxAge(Integer maxAge) {
this.maxAge = maxAge;
}
}
2.3 在xml中写拼接高级查询的sql语句
<!--对于下面的高级查询的条件这个可以提取出来,后面在高级查询的时候就可以使用
使用sql标签提取,然后在需要使用这一段代码的地方,直接使用Include标签引入-->
<sql id="whereSql">
<where>
<if test="name!=null and name!=''">
and name like concat("%",#{name},"%")
</if>
<if test="minAge!=null">
and age >= #{minAge}
</if>
<if test="maxAge!=null">
and age <= #{maxAge}
</if>
</where>
</sql>
<!--定义高级查询,需要接收高级查询的参数然后动态的拼接sql-->
<select id="query" parameterType="cn.lqq.query.EmployeeQuery" resultType="cn.lqq.domain.Employee">
select * from employee <include refid="whereSql"></include>
</select>
在接口EmployeeMapper 添加一个query方法:
List<Employee> query(EmployeeQuery query);
2.4 测试
@Test
public void testQuery()throws Exception{
EmployeeMapper mapper = MyBatiUtils.openSession().getMapper(EmployeeMapper.class);
EmployeeQuery query = new EmployeeQuery();
query.setName("1");
query.setMinAge(13);
query.setMaxAge(34);
List<Employee> list = mapper.query(query);
list.forEach(e-> System.out.println(e));
}
3.Mybatis 批量处理
3.1 批量添加
批量添加的sql,在mysql中可以使用下面的添加语句实现一条sql添加多条数据
INSERT INTO employee (name,age,sex) VALUES ('张三',23,true),('李四',34,false),('王五',25,true)
所以在xml中我们只需要拼接出上面的sql就可以实现批量添加,但是这种语法只能在mysql中使用,传过去一个集合,然后循环遍历value后面的值就可以了
<insert id="batchSave" parameterType="list">
insert into employee (name,age,sex) values
<foreach collection="list" item="employee" separator=",">
(#{employee.name},#{employee.age},#{employee.sex})
</foreach>
</insert>
在接口EmployeeMapper 添加一个batchSave方法:
//定义一个批量添加的方法,参数是一个list集合
void batchSave(List<Employee> list);
测试:
@Test
public void testBatchSave()throws Exception{
SqlSession session = MyBatiUtils.openSession();
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
List<Employee> list = Arrays.asList(new Employee("王五", 24, true),
new Employee("赵敏",34,false),
new Employee("小昭",45,false)
);
mapper.batchSave(list);
session.commit();
}
3.2 批量删除
批量删除的sql可以使用in关键字:delete from tableName where id in(1,2,3…)
可以传过去一个数组,也可以是个list,在xml中拼接的sql如下:
<delete id="batchDelete" parameterType="list">
delete from employee where id in
<foreach collection="list" item="d" separator="," open="(" close=")">
#{d}
</foreach>
</delete>
在接口EmployeeMapper 添加一个batchDelete方法:
//定义一个批量删除的方法,参数传递的是多个id值,可以使用集合也可以使用数组
void batchDelete(List<Long> list);
测试:
@Test
public void testBatchDelete()throws Exception{
SqlSession session = MyBatiUtils.openSession();
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
List<Long> list = new ArrayList<>();
list.add(61L);
list.add(62L);
list.add(63L);
mapper.batchDelete(list);
session.commit();
}
3.3 动态修改sql
3.3.1数据丢失问题:
在修改一条数据的,在我们只需要修改其中某几个字段时,其他不修改的字段,修改以后直接变成null,然后造成数据丢失的问题
解决方案:
1)在前台代码上写上隐藏域,所有字段都传递过去,这样没有修改的字段也会将原来的值传过去,这种方式不安全,且麻烦
2)修改我们的sql,没有修改的字段在写sql的时候不修改这些字段
3)先通过id查询到这条数据,然后再改里面需要修改的字段的值。因为修改之前先查询,所以性能不好
3.3.2 动态修改sql
修改的字段不确定,需要通过前台穿过来的字段来确定修改的值,这里就需要通过判断来动态生成sql
生成动态sql的xml代码如下:
<update id="dynamicUpdate" parameterType="cn.lqq.domain.Employee">
update employee
<set>
<if test="name!=null and name!=''">
name=#{name},
</if>
<if test="age!=null">
age=#{age},
</if>
<if test="sex!=null">
sex=#{sex}
</if>
</set>
where id=#{id}
</update>
在接口EmployeeMapper 添加一个dynamicUpdate方法:
//定义一个动态修改的方法
void dynamicUpdate(Employee employee);
测试:
@Test
public void testDynamicUpdate()throws Exception{
SqlSession session = MyBatiUtils.openSession();
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee employee = mapper.findOne(64L);
employee.setName("孙逊");
employee.setAge(43);
employee.setSex(false);
mapper.dynamicUpdate(employee);
session.commit();
}