Mybatis中传统Dao开发以及使用动态代理优化开发

学会MyBatis的CRUD基本操作其实也不是什么难事
MyBatis动态sql以及主配置文件中的内容

一、MyBatis对象分析

(1)Resources类

Resources类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的IO流对象。

(2)SqlSessionFactoryBuilder类

SqlSessionFactory的 创 建 , 需 要 使 用SqlSessionFactoryBuilder对 象 的build()方 法 。 由 于SqlSessionFactoryBuilder对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将该SqlSessionFactoryBuilder对象创建为一个方法内的局部对象,方法结束,对象销毁。

(3)SqlSessionFactory接口

SqlSessionFactory接口对象是一个重量级对象(系统开销大的对象),是线程安全的,所以一个应用只需要一个该对象即可。

创建SqlSession需要使用SqlSessionFactory接口的的openSession()方法。

  1. openSession(true):创建一个有自动提交功能的SqlSession

  2. openSession(false):创建一个非自动提交功能的SqlSession,需手动提交

  3. openSession():同openSession(false)

(4)SqlSession接口

SqlSession接口对象用于执行持久化操作。一个SqlSession对应着一次数据库会话,一次会话以SqlSession对象的创建开始,以SqlSession对象的关闭结束。

SqlSession接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其close()方法,将其关闭。再次需要会话,再次创建。SqlSession在方法内部创建,使用完毕后关闭。

二、创建工具类

(1)创建MyBatisUtil类

package com.lht.utils;

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;

public class MyBatisUtils {

    private static SqlSessionFactory factory = null;
    static {
        String config = "mybatis.xml";  //需要和项目中的文件名一样
        try {
            InputStream in = Resources.getResourceAsStream(config);
            //创建SqlSessionFactory对象,使用SqlSessionFactoryBuild
            factory = new SqlSessionFactoryBuilder().build(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //获取SqlSession的方法
    public static SqlSession getSqlSession(){
        SqlSession sqlSession = null;
        if( factory != null){
            sqlSession = factory.openSession(); //非自动提交事务
        }
        return sqlSession;
    }
}

(2)使用MyBatisUtil类

package com.lht;

import com.lht.domain.Student;
import com.lht.utils.MyBatisUtils;
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;
import java.util.List;

public class MyApp2 {
    public static void main(String[] args) throws IOException {

        //获取SqlSession对象,从SqlSessionFactory中获取SqlSession
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        //指定要执行的sql语句的标识,sql映射文件中的namespace + "." + 标签的id值
        String sqlId = "com.lht.dao.StudentDao.selectStudents";
        //执行sql语句,通过sqlId找到语句
        List<Student> studentList = sqlSession.selectList(sqlId);
        //输出结果
        studentList.forEach( stu -> System.out.println(stu));
        //9.关闭SqlSession对象
        sqlSession.close();
    }
}

三、MyBatis使用传统Dao开发方式

1. Dao开发

(1)创建Dao接口实现类

public class StudentDaoImpl implements StudentDao{}

(2)实现接口中select方法

 @Override
    public List<Student> selectStudents() {

        //获取SqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        String sqlId = "com.lht.dao.StudentDao.selectStudents";
        //执行sql语句,使用SqlSession类的方法
        List<Student> students = sqlSession.selectList(sqlId);
        //关闭
        sqlSession.close();
        return students;
    }

测试查询操作:

MyBatisTest类中创建testSelectStudents方法

package com.lht;

import com.lht.dao.StudentDao;
import com.lht.dao.impl.StudentDaoImpl;
import com.lht.domain.Student;
import org.junit.Test;

import java.util.List;

public class TestMybatis {
    @Test
    public void testSelectStudents(){
        StudentDao dao = new StudentDaoImpl();
        List<Student> studentList = dao.selectStudents();
        for(Student stu : studentList){
            System.out.println(stu);
        }
    }
}

(3)实现接口中insert方法

public int insertStudent(Student student) {
	SqlSession session = MyBatisUtil.getSqlSession();
	int nums = session.insert("com.lht.dao.StudentDao.insertStudent",student);
	session.commit();
	session.close();
	return nums;
}

测试insert

@Test
public void testInsert() throws IOException {
	Student student = new Student();
	student.setId(1006);
	student.setName("林浩");
	student.setEmail("linhao@163.com");
	student.setAge(26);
	int nums = studentDao.insertStudent(student);
	System.out.println("使用Dao添加数据:"+nums);
}

(4)实现接口中update方法

public int updateStudent(Student student) {
	SqlSession session = MyBatisUtil.getSqlSession();
	int nums = session.insert("com.lht.dao.StudentDao.updateStudent",student);
	session.commit();
	session.close();
	return nums;
}

测试update

@Test
public void testUpdate()throws IOException {
	Student student = new Student();
	student.setId(1006);
	student.setAge(28);
	int nums = studentDao.updateStudent(student);
	System.out.println("使用Dao修改数据:"+nums);}

(5)实现接口中delete方法

public int deleteStudent(int id) {
	SqlSession session = MyBatisUtil.getSqlSession();
	int nums = session.insert("com.lht.dao.StudentDao.deleteStudent",1006);
	session.commit();
	session.close();
	return nums;
}

测试delete

@Test
public void testDelete() throws IOException {
	int nums = studentDao.
	deleteStudent(1006);
	System.out.println("使用Dao修改数据:"+nums);
}
2. 传统Dao开发方式的分析

在前面例子中自定义Dao接口实现类时发现一个问题:Dao的实现类其实并没有干什么实质性的工作,它仅仅就是通过SqlSession的相关API定位到映射文件mapper中相应id的SQL语句,真正对DB进行操作的工作其实是由框架通过mapper中的SQL完成的。

所以,MyBatis框架就抛开了Dao的实现类,直接定位到映射文件mapper中的相应SQL语句,对DB进行操作。这种对Dao的实现方式称为Mapper的动态代理方式。

Mapper动态代理方式无需程序员实现Dao接口。接口是由MyBatis结合映射文件自动生成的动态代理实现的。

动态代理原理
  1. dao对象类型是StudentDao,全限定名称:com.lht.dao.StudentDaonamespace是一样的。

  2. 方法名称:selectStudents,这个方法就是mapper文件中的idselectStudents

  3. 通过dao中方法的返回值也可以确定MyBatis要调用的SqlSession的方法

    如果返回值是list,调用的是SqlSession.selectList()方法

    如果返回值是int,或是非List,看mapper文件中的标签是<insert>,<update>就会调用SqlSessioninsert,update方法

四、MyBatis框架Dao代理

1. Dao代理实现CURD

(1)去掉Dao接口实现类

在这里插入图片描述

(2)getMapper获取代理对象

只需调用SqlSession的getMapper()方法,即可获取指定接口的实现类对象。该方法的参数为指定Dao接口类的class值。

SqlSession session = factory.openSession();
StudentDao dao = session.getMapper(StudentDao.class);

使用工具类

StudentDao studentDao = MyBatisUtil.getSqlSession().getMapper(StudentDao.class);

(3)使用Dao代理对象方法执行sql语句

select方法

@Test
public void testSelect() throws IOException {
	final List<Student> studentList = studentDao.selectStudents();
	studentList.forEach( stu -> System.out.println(stu));
}

insert方法

@Test
public void testInsert() throws IOException {
	Student student = new Student();
	student.setId(1006);
	student.setName("林浩");
	student.setEmail("linhao@163.com");
	student.setAge(26);
	int nums = studentDao.insertStudent(student);
	System.out.println("使用Dao添加数据:"+nums);
}

update方法

@Test
public void testUpdate() throws IOException {
	Student student = new Student();
	student.setId(1006);
	student.setAge(28);
	int nums = studentDao.updateStudent(student);
	System.out.println("使用Dao修改数据:"+nums);
}

delete方法

@Test
public void testDelete() throws IOException {
	int nums = studentDao.deleteStudent(1006);
	System.out.println("使用Dao修改数据:"+nums);
}
2. 深入理解参数

(1)parameterType

parameterType: 接口中方法参数的类型,类型的完全限定名或别名。

这个属性是可选的,因为MyBatis 可以推断出具体传入语句的参数,默认值为未设置(unset)。接口中方法的参数从java代码传入到mapper文件的sql语句。

别名或全限定名称:

int 或java.lang.Integer

hashmap 或java.util.HashMap

list 或java.util.ArrayList

student 或com.bjpowernode.domain.Student

<delete id="deleteStudent" parameterType="int">
	delete from student where id=#{studentId}
</delete>

//等同于
<delete id="deleteStudent" parameterType="java.lang.Integer">
	delete from student where id=#{studentId}
</delete>

(2)传递 一个简单参数

简单类型:mybatis把java的基本数据类型和String都较简单类型。

在mapper文件获取简单类型的一个参数的值,使用#{任意字符}和方法的参数名无关。

接口方法

Student selectById(int id);

mapper文件

<select id="selectById" resultType="com.lht.domain.Student">
	select id,name,email,age from student where id=#{studentId}
</select>
//#{studentId},studentId是自定义的变量名称,和方法参数名无关

测试方法

public void testSelectById(){
	//一个参数
	Student student = studentDao.selectById(1005);
	System.out.println("查询id是1005的学生:"+student);
}
使用#{}之后,mybatis执行sql是使用的jdbc中的PreparedStatement对象
(MyBatis内部机制)由mybatis执行下面的代码:

1. mybatis创建Connection,PreparedStatement对象
	String sql = "select * from student where id=?";
	PreparedStatement pst = conn.preparedStatement(sql);
	pst.setInt(1,1001);

2. 执行sql封装为resultType="com.lht.domain.Student"这个对象
	ResultSet rs = ps.executeQuery();
	Student studnet = null;
	while(rs.next()){
		//从数据库取表的一行数据,存到一个jdbc对象属性中
		student = new Student();
		student.setId(rs.getInt("id"));
		student.setName(rs.getString("name"));
		student.setEmail(rs.getString("email"));
		student.setAge(rs.getInt("age"));
	}
	return student;	//给了dao方法调用的返回值
	

(3)多个参数-使用@Param

Dao接口方法多个参数,需要通过名称使用参数。在方法形参前面加入@Param(“自定义参数名”)mapper文件使用#{自定义参数名}

接口方法

List<Student> selectMulitParam(@Param("myname") String name,
                                   @Param("myage") Integer age);

mapper文件

<select id="selectMultiParam" resultType="com.lht.domain.Student">
     select * from studnet where name=#{myname} or age=#{myage}
</select>

测试方法

 @Test
    public void testSelectMultiParam(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
        List<Student> students = dao.selectMultiParam("里斯",20);
        for(Student student : students){
            System.out.println("学生="+ student);
        }
        sqlSession.close();
    }

(4)多个参数-使用对象

使用java对象传递参数,java的属性值就是sql需要的参数值。每一个属性就是一个参数。语法格式:#{property,javaType=java中数据类型名,jdbcType=数据类型名称}

javaType, jdbcType的类型MyBatis可以检测出来,一般不需要设置。

常用格式#{property}

创建保存参数值的对象QueryParam

package com.lht.vo;

public class QueryParam {

    private String paramName;
    private Integer paramAge;

    public String getParamName() {
        return paramName;
    }

    public void setParamName(String paramName) {
        this.paramName = paramName;
    }

    public Integer getParamAge() {
        return paramAge;
    }

    public void setParamAge(Integer paramAge) {
        this.paramAge = paramAge;
    }
}

接口方法

List<Student> selectMultiObject(QueryParam queryParam);

mapper文件

 <select id="selectMultiObject" resultType="com.lht.domain.Student">
    select * from student where name=#{paramName,javaType=java.lang.String,jdbcType=VARCHAR}
   	or age=#{paramAge,javaType=java.lang.Integer,jdbcType=INTEGER}
 </select>
//或者
<select id="selectMutiObject" resultType="com.lht.domain.Student">
	select * from student where name=#{paramName} or age=#{paramAge}
</select>

测试方法

@Test
    public void testSelectMultiObject(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
        QueryParam param = new QueryParam();
        param.setParamName("张三");
        param.setParamAge(24);
        List<Student> students = dao.selectMultiObject(param);
        for(Student student : students){
            System.out.println("学生="+student);
        }
        sqlSession.close();
    }

(5)多个参数-按位置

参数位置从0开始,引用参数语法#{arg位置},第一个参数是#{arg0}, 第二个是#{arg1}
注意:mybatis-3.3版本和之前的版本使用#{0},#{1}方式,从mybatis3.4开始使用#{arg0}方式。

接口方法

List<Student> selectByNameAndAge(String name,int age);

mapper文件

<select id="selectByNameAndAge" resultType="com.lht.doamin.Studnet">
	select * from Studnet where name=#{arg0} or age=#{arg1}
</select>

测试方法

public void testSelectByNameAndAge(){
	SqlSession sqlsession = MyBatisUtils.getSqlSession();
	StudnetDao dao = sqlSession.getMapper(StudentDao.class);
	List<Student> stuList = studentDao.selectByNameAndAge("李四",20);
	stuList.forEach( stu -> System.out.println(stu));
}

(6)多个参数-使用Map

Map集合可以存储多个值,使用Mapmapper文件一次传入多个参数。Map集合使用StringkeyObject类型的值存储参数。mapper文件使用#{key}引用参数值。

接口方法

List<Student> selectMultiMap(Map<String,Object> map);

mapper文件

<select id="selectMultiMap" resultType="com.lht.domain.Student">
	select * from student where name=#{myname} or age=#{myage}
</select>

测试方法

public void testSelectMultiMap(){
	SqlSession sqlSession = MyBatisUtils.getSqlSession();
	StudentDao dao = sqlSession.getMapper(StudentDao.class);
	Map<String,Object> data = new Hashmap<>();
	data.put("myname","王五");
	data.put("myage",20);
	List<Student> stuList = studentDao.selectMutltiMap(data);
	stuList.foreach(stu -> System.out.println(stu));
}
3. #和$

#:占位符,告诉mybatis使用实际的参数值代替。并使用PrepareStatement对象执行sql语句,#{...}代替sql语句的“?”。这样做更安全,更迅速,通常也是首选做法。

mapper文件

<select id="selectById" resultType="com.lht.domain.Student">
	select * from student where id=#{studentId}
</select>

转为MyBatis的执行是:

String sql=”select id,name,email,age from student where id=?;
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1,1005);

解释

where id=? 就是where id=#{studentId}
ps.setInt(1,1005) , 1005会替换掉#{studentId}

$:字符串替换,告诉mybatis使用 $ 包含的“字符串”替换所在位置。使用Statementsql语句和${}的内容连接起来。主要用在替换表名,列名,不同列排序等操作。可能会造成安全性问题。

例子:

分别使用id,email列查询Student

接口方法

 Student findById(int id);
    Student findByEmail(String email);

mapper文件

<select id="findById" resultType="com.lht.domain.Student">
        select * from student where id=#{studentId}
</select>

<select id="findByEmail" resultType="com.lht.domain.Student">
        select * from student where email=#{studentEmail}
select>

测试方法

 @Test
    public void testFindStudent(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
        Student stu1 = dao.findById(1002);
        System.out.println("findById:"+ stu1);

        Student stu2 = dao.findByEmail("lisi@qq.com");
        System.out.println("findByEmail:"+stu2);
    }

通用方法,使用不同列作为查询条件

接口方法

Student findByDiffField(@Param("col") String colunName,@Param("cval") Object value);

mapper文件

<select id="findByDiffField" resultType="com.lht.domain.Student">
        select * from student where ${col} = #{cval}
</select>

测试方法

 @Test
    public void testFindDiffField(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
        Student stu1 = dao.findByDiffField("id",1002);
        System.out.println("按id列查询:"+stu1);
        Student stu2 = dao.findByDiffField("email","lisi@qq.com");
        System.out.println("按email查询:"+stu2);
    }

五、封装Mybatis输出结果

1. resultType

执行 sql 得到 ResultSet 转换的类型,使用类型的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。

resultType结果类型,指sql语句执行完毕后,数据转为java对象(java类型是任意的)

处理方式:

  1. mybatis执行sql语句,然后mybatis调用类的无参构造方法,创建对象。
  2. mybatis把ResultSet指定列付给同名的属性

在这里插入图片描述

接口方法

ViewStudent selectStudentReturnViewStudent(@Param("sid") Integer id);

mapper文件

<select id="selectStudentReturnViewStudent" resultType="com.lht.vo.ViewStudent">
        select id,name from student where id=#{sid}
</select>

测试方法

@Test
    public void testeturnViewStudent(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
        ViewStudent student = dao.selectStudentReturnViewStudent(1002);
        System.out.println("1002 student:"+student);
    }

resultType结果类型的值:

  1. 类型的全限定名称
  2. 类型的别名,例如java.lang.Integer别名是int

别名定义:

在主配置文件中
<!--定义别名-->
    <typeAliases>
        <!--第一种方式-->
        <!--可以指定一个类型,一个自定义别名
            type:自定义类型的全限定名称
            alias:别名(容易记忆)
        -->
        <typeAlias type="com.lht.vo.ViewStudent" alias="vstu" />
        <typeAlias type="com.lht.domain.Student" alias="stu" />

        <!--第二种方式-->
        <!--<package> name是包名,这个包中的所有类,类名就是别名(类名不区分大小写)-->
        <package name="com.lht.domain"/>
        <package name="com.lht.vo"/>
    </typeAliases>
resultType的三种类型

A、简单类型

接口方法:

int countStudent();

mapper 文件:

<select id="countStudent" resultType="int">
 select count(*) from student
</select>

测试方法:

@Test
public void testRetunInt(){
 SqlSession sqlSession = MyBatisUtils.getSession();
 StudentDao dao = sqlSession.getMapper(StudentDao.class);
 int count = studentDao.countStudent();
 System.out.println("学生总人数:"+ count);
}

B、 对象类型

接口方法:

Student selectById(int id);

mapper 文件:

<select id="selectById" resultType="Student">
 select id,name,email,age from student where id=#{studentId}
</select>

测试方法

@Test
    public void testSelectStudentById(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
       Student student = dao.selectStudentById(1002);
        System.out.println("student=" + student);
    }

C、 Map

sql 的查询结果作为 Mapkeyvalue。推荐使用 Map<Object,Object>
注意:Map 作为接口返回值,sql 语句的查询结果最多只能有一条记录。大于一条记录是错误。

接口方法:

Map<Object,Object> selectReturnMap(int id);

mapper 文件:

<select id="selectReturnMap" resultType="java.util.HashMap">
 select name,email from student where id = #{studentId}
</select>

测试方法:

@Test
public void testReturnMap(){
 SqlSession sqlSession = MyBatisUtils.getSqlSession();
 StudentDao dao = sqlSession.getMapper(StudentDao.class);
 Map<Object,Object> retMap = studentDao.selectReturnMap(1002);
 System.out.println("查询结果是 Map:"+retMap);
}
2. resultMap

resultMap 可以自定义 sql 的结果和 java 对象属性的映射关系。更灵活的把列值赋值给指定属性。

常用在列名和 java 对象属性名不一样的情况。

使用方式:

  1. 先定义 resultMap,指定列名和属性的对应关系。
  2. 在中把 resultType 替换为 resultMap。

接口方法:

List<Student> selectUseResultMap(QueryParam param);

mapper 文件:

<!-- 创建 resultMap
 id:自定义的唯一名称,在<select>使用
 type:期望转为的 java 对象的全限定名称或别名
-->
<resultMap id="studentMap" type="com.bjpowernode.domain.Student">
 <!-- 主键字段使用 id -->
 <id column="id" property="id" />
 <!--非主键字段使用 result-->
 <result column="name" property="name"/>
 <result column="email" property="email" />
 <result column="age" property="age" />
</resultMap>
<!--resultMap: resultMap 标签中的 id 属性值-->
<select id="selectUseResultMap" resultMap="studentMap">
 select id,name,email,age from student where name=#{queryName} or
age=#{queryAge}
</select>

测试方法:

@Test
public void testSelectUseResultMap(){
 SqlSession sqlSession = MyBatisUtils.getSqlSession();
 StudnetDao dao = sqlSession.getMapper(StudentDao.class);
 QueryParam param = new QueryParam();
 param.setQueryName("李力");
 param.setQueryAge(20);
 List<Student> stuList = studentDao.selectUseResultMap(param);
 stuList.forEach( stu -> System.out.println(stu));
}

如果还是在列明和属性名不一致的情况下,想使用resultMap可以在sql中给列起列别名。

<!--stuid,stuname,stuage是定义在MyStudent类中的属性名-->
<select id="selectDiffColProperty" result="com.lht.domain.MyStudent">
	select id as stuid ,name as stuname, email as stuemail ,age as stuage from student 
</select>
3. 模糊查询

模糊查询的实现有两种方式, 一是 java 代码中给查询数据加上“%” ; 二是在 mapper 文件 sql 语句的条件位置加上“%

(1)在java代码中给查询数据加%

接口方法:

List<Student> selectLike1(String name);

mapper 文件:

<select id="selectLike1" resultType="com.lht.domain.Student">
        select * from student where name like #{studentName}
</select>

测试方法:

 @Test
    public void testSelectLike1(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
        String name = "%李%";
        List<Student> stuList = dao.selectLike1(name);
        for(Student stu : stuList){
            System.out.println("#######学生="+stu);
        }
    }

(2)在mapper文件的sql语句的条件位置上加%

接口方法:

List<Student> selectLike2(String name);

mapper 文件:

<select id="selectLike2" resultType="com.lht.domain.Student">
        select * from student where name like "%" #{studentName} "%"
</select>

测试方法:

 @Test
    public void testSelectLike2(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
        String name = "李";
        List<Student> stuList = dao.selectLike2(name);
        for(Student stu : stuList){
            System.out.println("#######学生="+stu);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值