【Mybatis从入门到实战教程】第二章 Mybatis DAO开发
二、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 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 select() { SqlSession sqlSession = MybatisUtil.getSession(); List 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文件
select deptno,dname,loc from dept select deptno,dname,loc from dept where deptno=#{deptno} insert into dept(dname,loc) values(#{dname},#{loc}) update dept set dname=#{dname},loc=#{loc} where deptno=#{deptno} delete from dept where deptno=#{deptno}
加载mapper文件:
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 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 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文件
select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp select empno,ename,job,hiredate,mgr,sal,comm,deptno from emp where empno=#{empno} insert into emp(ename,job,mgr,hiredate,sal,comm,deptno) values(#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno}) update emp set ename=#{ename},job=#{job},mgr=#{mgr},hiredate=#{hiredate},sal=#{sal},comm=#{comm},deptno=#{deptno} where empno=#{empno} delete from emp where empno=#{empno}
加载mapper文件:
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 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的通用性。