二、Mybatis DAO开发
使用Mybatis开发Dao,通常有两个方法,即原始Dao开发方法和Mapper接口开发方法。
2.1 Mybatis API
2.1.1 SqlSessionFactoryBuilder
SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,因为SqlSession是通过SqlSessionFactory生产,所以可以将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。
2.1.2 SqlSessionFactory
SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory。
注:openSession(true),true为自动提交事务,false则相反。
2.1.3 SqlSession
SqlSession是一个面向用户(程序员)的接口,其中提供了很多操作数据库的方法。如:selectOne(返回单个对象)、selectList(返回单个或多个对象)、insert、update、delete。
SqlSession的实例不能共享使用,它是线程不安全的,每个线程都应该有它自己的SqlSession实例,因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
2.2 Mybatis工具类
为了简化MyBatis的开发,可将MyBatis进一步封装。
package com.newcapec.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
/**
* Mybatis工具类
*/
public class MybatisUtil {
/**
* 不让用户在外界创建工具类对象
*/
private MybatisUtil() {
}
/**
* 初始化SqlSessionFactory对象
*/
private static SqlSessionFactory factory;
static {
try {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取SqlSession对象的方法
*/
public static SqlSession getSession() {
return factory.openSession();
}
}
测试:
public class MybatisUtilTest {
@Test
public void test() {
SqlSession sqlSession = MybatisUtil.getSession();
System.out.println(sqlSession);
sqlSession.close();
}
}
2.3 原始DAO开发方式
原始Dao开发方法需要程序员编写Dao接口和Dao实现类,无非就是Dao实现类里面调用映射文件里面定义的sql而已。
2.3.1 实体类
package com.newcapec.entity;
/**
* Dept实体类
*/
public class Dept {
private Integer deptno;
private String dname;
private String loc;
public Integer getDeptno() {
return deptno;
}
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
@Override
public String toString() {
return "Dept{" +
"deptno=" + deptno +
", dname='" + dname + '\'' +
", loc='" + loc + '\'' +
'}';
}
}
2.3.2 接口
package com.newcapec.dao;
import com.newcapec.entity.Dept;
import java.util.List;
public interface DeptDao {
List<Dept> select();
Dept selectById(Integer deptno);
int insert(Dept dept);
int update(Dept dept);
int delete(Integer deptno);
}
2.3.3 实现类
package com.newcapec.dao.impl;
import com.newcapec.dao.DeptDao;
import com.newcapec.entity.Dept;
import com.newcapec.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
/**
* DeptDao实现类
*/
public class DeptDaoImpl implements DeptDao {
@Override
public List<Dept> select() {
SqlSession sqlSession = MybatisUtil.getSession();
List<Dept> list = sqlSession.selectList("dept.select");
sqlSession.close();
return list;
}
@Override
public Dept selectById(Integer deptno) {
SqlSession sqlSession = MybatisUtil.getSession();
Dept dept = sqlSession.selectOne("dept.selectById", deptno);
sqlSession.close();
return dept;
}
@Override
public int insert(Dept dept) {
SqlSession sqlSession = MybatisUtil.getSession();
int result = sqlSession.insert("dept.insert", dept);
sqlSession.commit();
sqlSession.close();
return result;
}
@Override
public int update(Dept dept) {
SqlSession sqlSession = MybatisUtil.getSession();
int result = sqlSession.update("dept.update", dept);
sqlSession.commit();
sqlSession.close();
return result;
}
@Override
public int delete(Integer deptno) {
SqlSession sqlSession = MybatisUtil.getSession();
int result = sqlSession.delete("dept.delete", deptno);
sqlSession.commit();
sqlSession.close();
return result;
}
}
2.3.4 mapper文件
<?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">
<mapper namespace="dept">
<select id="select" resultType="com.newcapec.entity.Dept">
select deptno,dname,loc from dept
</select>
<select id="selectById" parameterType="java.lang.Integer" resultType="com.newcapec.entity.Dept">
select deptno,dname,loc from dept where deptno=#{deptno}
</select>
<insert id="insert" parameterType="com.newcapec.entity.Dept">
insert into dept(dname,loc) values(#{dname},#{loc})
</insert>
<update id="update" parameterType="com.newcapec.entity.Dept">
update dept set dname=#{dname},loc=#{loc} where deptno=#{deptno}
</update>
<delete id="delete" parameterType="java.lang.Integer">
delete from dept where deptno=#{deptno}
</delete>
</mapper>
加载mapper文件:
<!-- 加载映射文件的位置 -->
<mappers>
<mapper resource="mapper/Dept.xml"/>
</mappers>
2.3.5 测试
package com.newcapec;
import com.newcapec.dao.DeptDao;
import com.newcapec.dao.impl.DeptDaoImpl;
import com.newcapec.entity.Dept;
import org.junit.Test;
import java.util.List;
public class DaoTest {
private DeptDao deptDao = new DeptDaoImpl();
@Test
public void testSelect() {
List<Dept> list = deptDao.select();
for (Dept dept : list) {
System.out.println(dept);
}
}
@Test
public void testSelectById() {
Dept dept = deptDao.selectById(20);
System.out.println(dept);
}
@Test
public void testInsert() {
Dept dept = new Dept();
dept.setDname("企划部");
dept.setLoc("深圳");
int result = deptDao.insert(dept);
System.out.println("影响数据库的条数:" + result);
}
@Test
public void testUpdate() {
Dept dept = new Dept();
dept.setDeptno(41);
dept.setDname("生产部");
dept.setLoc("杭州");
int result = deptDao.update(dept);
System.out.println("影响数据库的条数:" + result);
}
@Test
public void testDelete() {
int result = deptDao.delete(41);
System.out.println("影响数据库的条数:" + result);
}
}
2.3.6 原始DAO开发问题
dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量。
调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不利于开发维护。
调用SqlSession方法时传入的变量,由于SqlSession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。
注:原始Dao开发和我们Web阶段讲解的Dao开发基本类似,都是有Dao接口和Dao实现类,无非Web阶段的Dao实现类中通过DBUtils来操作SQL;现在Mybatis的原始Dao开发,把SQL分离出去了,写在的XML映射文件里面而已。
2.4 Mapper代理方式(重点)
Mapper代理开发方式只需要程序员编写Mapper接口(相当于Dao接口),由MyBatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
程序员编写Mapper接口需要遵循一些开发规范,MyBatis可以自动生成Mapper接口实现类代理对象。
2.4.1 开发规范
1、Mapper.xml文件中的namespace与mapper接口的类路径相同。
2、Mapper接口方法名和Mapper.xml中定义的每个标签的id相同。
3、Mapper接口方法的参数类型和mapper.xml中定义的每个sql的parameterType的类型相同。
4、Mapper接口方法返回值类型和mapper.xml中定义的每个sql的resultType的类型相同。
注:Mapper.xml映射文件最好和Mapper接口名称一致;
2.4.2 实体类
package com.newcapec.entity;
import java.util.Date;
/**
* Emp实体类
*/
public class Emp {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
public Integer getEmpno() {
return empno;
}
public void setEmpno(Integer empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Integer getMgr() {
return mgr;
}
public void setMgr(Integer mgr) {
this.mgr = mgr;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
public Double getComm() {
return comm;
}
public void setComm(Double comm) {
this.comm = comm;
}
public Integer getDeptno() {
return deptno;
}
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", mgr=" + mgr +
", hiredate=" + hiredate +
", sal=" + sal +
", comm=" + comm +
", deptno=" + deptno +
'}';
}
}
2.4.3 Mapper接口
package com.newcapec.mapper;
import com.newcapec.entity.Emp;
import java.util.List;
/*
* Mapper接口相当于我们之前写的Dao接口,只是在Mybatis里面我们习惯这么写而已。
*/
public interface EmpMapper {
List<Emp> select();
Emp selectById(Integer empno);
void insert(Emp emp);
int update(Emp emp);
boolean delete(Integer empno);
}
-
批量查询:方法返回值为List类型,表示SqlSession对象将调用selectList()方法。
-
单条查询:方法返回值为单个实体对象,表示SqlSession对象将调用selectOne()方法。
-
增删改:
-
方法返回值为void,表示SqlSession对象中insert,update,delete方法的返回值不做任何处理。
-
方法返回值为int类型,表示SqlSession对象中insert,update,delete方法的返回值直接返回。
-
方法返回值为boolean类型,表示根据SqlSession对象中的insert,update,delete方法返回值(影响数据库的条数)判断操作是否成功,如果影响数据库的条数大于0条,表示成功,否则表示失败。
-
2.4.4 mapper文件
<?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接口的全限定名保持一致-->
<mapper namespace="com.newcapec.mapper.EmpMapper">
<!--
statementId与Mapper接口的方法名称保持一致
parameterType的类型必须与方法的参数类型保持一致
resultType的类型必须与方法的返回值类型保持一致
-->
<select id="select" resultType="com.newcapec.entity.Emp">
select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp
</select>
<select id="selectById" parameterType="java.lang.Integer" resultType="com.newcapec.entity.Emp">
select empno,ename,job,hiredate,mgr,sal,comm,deptno from emp where empno=#{empno}
</select>
<insert id="insert" parameterType="com.newcapec.entity.Emp">
insert into emp(ename,job,mgr,hiredate,sal,comm,deptno)
values(#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno})
</insert>
<update id="update" parameterType="com.newcapec.entity.Emp">
update emp set
ename=#{ename},job=#{job},mgr=#{mgr},hiredate=#{hiredate},sal=#{sal},comm=#{comm},deptno=#{deptno}
where empno=#{empno}
</update>
<delete id="delete" parameterType="java.lang.Integer">
delete from emp where empno=#{empno}
</delete>
</mapper>
加载mapper文件:
<!-- 加载映射文件的位置 -->
<mappers>
<mapper resource="mapper/EmpMapper.xml"/>
</mappers>
2.4.5 测试
package com.newcapec;
import com.newcapec.entity.Emp;
import com.newcapec.mapper.EmpMapper;
import com.newcapec.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.Date;
import java.util.List;
public class MapperTest {
private SqlSession sqlSession;
private EmpMapper empMapper;
@Before
public void before() {
sqlSession = MybatisUtil.getSession();
//获取Mapper接口的代理对象
empMapper = sqlSession.getMapper(EmpMapper.class);
}
@After
public void after() {
sqlSession.commit();
sqlSession.close();
}
@Test
public void test() {
System.out.println(sqlSession);
System.out.println(empMapper);
}
@Test
public void testSelect() {
List<Emp> list = empMapper.select();
for (Emp emp : list) {
System.out.println(emp);
}
}
@Test
public void testSelectById() {
Emp emp = empMapper.selectById(7938);
System.out.println(emp);
}
@Test
public void testInsert() {
Emp emp = new Emp();
emp.setEname("小明");
emp.setJob("职员");
emp.setSal(4500.0);
emp.setComm(1000.0);
emp.setMgr(1);
emp.setHiredate(new Date());
empMapper.insert(emp);
}
@Test
public void testUpdate() {
Emp emp = new Emp();
emp.setEmpno(7940);
emp.setEname("小李");
emp.setJob("秘书");
emp.setSal(5300.0);
emp.setComm(1300.0);
emp.setMgr(1);
emp.setHiredate(new Date());
int result = empMapper.update(emp);
System.out.println("方法的返回值:" + result);
}
@Test
public void testDelete() {
boolean result = empMapper.delete(7940);
System.out.println("方法的返回值:" + result);
}
}
Mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用pojo包装对象或map对象,保证dao的通用性。