MyBatis入门17-37:使用动态代理和传参规范

开始时间:2021-12-06
课程链接:动力节点MyBatis

传统dao的实现

过程和之前一样,可以选择复制
在这里插入图片描述

因为每次写StudentDao都差不多,所以我们可以设置一个模板来实现快捷生成
File-Setting-File and Code Templates
新建一个未命名的 unnamed模板,自己命名,将要重复使用的部分复制进去,后缀名改为xml
在这里插入图片描述
同理resources下的mybatis.xml也可以这样生成(先设置好该模板)
在这里插入图片描述
我们之前的测试,是没怎么用上StudentDao这个接口的
现在其他大部分代码不变,设置一个该接口的实现类

在这里插入图片描述

package BUPT.dao.implement;

import BUPT.dao.StudentDao;
import BUPT.entity.Student;
import BUPT.util.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class StudentDaoImp implements StudentDao {
    @Override
    public List<Student> selectStudents() {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        String sqlId = "BUPT.dao.StudentDao.selectStudents";
        List<Student> students = sqlSession.selectList(sqlId);
        sqlSession.close();
        return students;
    }

    @Override
    public int insertStudent(Student student) {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        String sqlId = "BUPT.dao.StudentDao.insertStudent";
        sqlSession.commit();
        int num = sqlSession.insert(sqlId, student);
        sqlSession.close();
        return num;
    }
}

再写一个测试类

package BUPT;

import static org.junit.Assert.assertTrue;

import BUPT.dao.StudentDao;
import BUPT.dao.implement.StudentDaoImp;
import BUPT.entity.Student;
import org.junit.Test;

import java.util.List;

/**
 * Unit test for simple App.
 */
public class AppTest {
    @Test
    public void testSelectStudents() {
        StudentDao dao = new StudentDaoImp();
        List<Student> students = dao.selectStudents();
        for (Student student : students) {
            System.out.println(student);
        }
    }
    @Test
    public void testInsertStudent() {
        StudentDao dao = new StudentDaoImp();
        Student student = new Student();
        student.setAge(30);
        student.setEmail("yaoming@qq.com");
        student.setId(1007);
        student.setName("yaoming");
        int num = dao.insertStudent(student);
        System.out.println("添加对象个数为" + num);
    }
}

这样才算把这个接口用上。

但是还是有不足的地方
测试类代码依然有不少重复,解决办法就是使用动态代理

动态代理的实现

在上面的例子中,实现方法主要是以下代码
单独写了一个implement类

    public List<Student> selectStudents() {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        String sqlId = "BUPT.dao.StudentDao.selectStudents";
        List<Student> students = sqlSession.selectList(sqlId);
        sqlSession.close();
        return students;
    }

然后测试类对应的写

    public void testSelectStudents() {
        StudentDao dao = new StudentDaoImp();
        List<Student> students = dao.selectStudents();
        for (Student student : students) {
            System.out.println(student);
        }
    }

当我的方法变多了以后,就得在StudentDaoImp类里面继续去实现StudentDao类新的方法
并且在测试类中,新建StudentDaoImp类

我们考虑使用动态代理对其进行简化
参考博客
动态代理的好处

动态代理: 在程序执行过程中,使用jdk的反射机制,创建代理类对象, 并动态的指定要代理目标类。
换句话说: 动态代理是一种创建java对象的能力,不用创建TaoBao类,就能创建代理类对象。
动态:在程序执行时,调用jdk提供的方法才能创建代理类的对象。

也就是说,我们不需要写StudentDaoImp类,利用动态代理实现其效果的替代

删除StudentDaoImp类
改写测试类的代码为
getMapper(dao接口)实现对象的创建,这个对象就能执行dao对象的所有方法
mapper根据命名空间自己去寻找对应的方法

<mapper namespace="BUPT.dao.StudentDao">
public void testSelectStudents(){
        /**
         * 使用mybatis的动态代理机制, 使用SqlSession.getMapper(dao接口)
         * 参数是接口的class
         * getMapper能获取dao接口对于的实现类对象。
         */
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao dao  =  sqlSession.getMapper(StudentDao.class);

        //com.sun.proxy.$Proxy2 : jdk的动态代理
        System.out.println("dao="+dao.getClass().getName());
        //调用dao的方法, 执行数据库的操作
        List<Student> students = dao.selectStudents();
        for(Student stu: students){
            System.out.println("学生="+stu);
        }
    }

同理写出插入的方法

@test
public void testInsertStudent(){
        SqlSession sqlSession  = MyBatisUtils.getSqlSession();
        StudentDao dao  =  sqlSession.getMapper(StudentDao.class);

        Student student = new Student();
        student.setId(1008);
        student.setName("李飞");
        student.setEmail("dunshan@qq.com");
        student.setAge(28);
        int nums = dao.insertStudent(student);
        sqlSession.commit();
        System.out.println("添加对象的数量:"+nums);
    }

值得一提的是,pom.xml文件中的内容,一定要和module名.xml文件的内容一致,版本要一样
在这里插入图片描述
通过我们的接口名和方法名,映射到对应xml文件中的sql语句,知道了sql语句就知道了返回值类型

传参

当然也可以写

<select id="selectStudentById" parameterType="java.lang.Integer" resultType="BUPT.entity.Student">

通过parameterType来决定,但不是强制要写的。

注意:简单类型的传参,使用#{待传参参数名}

多个参数传参(比如SQL语句涉及多个字段的查询),使用@param注解命名参数

StudentDao

public interface StudentDao {

    public Student selectStudentById(Integer id);

    public Student selectStudentByTwoParams(@Param("myid") Integer id, @Param("myage") Integer age);
}

StudentDao.xml

    <select id="selectStudentByTwoParams"  resultType="BUPT.entity.Student">
        select id, name, email, age
        from student
        where id = #{myid} and age=#{myage}
    </select>

Test

 public void testSelectStudentByTwoParams() {
        /**
         * 使用mybatis的动态代理机制, 使用SqlSession.getMapper(dao接口)
         * 参数是接口的class
         * getMapper能获取dao接口对于的实现类对象。
         */
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);

        //com.sun.proxy.$Proxy2 : jdk的动态代理
        System.out.println("dao=" + dao.getClass().getName());
        //调用dao的方法, 执行数据库的操作
        //传入的1002是整型值。由我们写的StudentDao.xml文件写的parameterType决定,但不是强制要写的
        //<select id="selectStudentById" parameterType="java.lang.Integer" resultType="BUPT.entity.Student">

        Student student = dao.selectStudentByTwoParams(1002, 18);
        System.out.println(student);
    }

使用对象传参

也就是上面插入新对象时的方法
StudentDao

public interface StudentDao {
    int insertStudent(Student student);
}

StudentDao.xml

    <insert id="insertStudent">
        <!--起到占位符的作用-->
        insert into student values(#{id},#{name},#{email},#{age})
    </insert>

测试类

    public void testInsertStudent(){
        SqlSession sqlSession  = MyBatisUtils.getSqlSession();
        StudentDao dao  =  sqlSession.getMapper(StudentDao.class);

        Student student = new Student();
        student.setId(1008);
        student.setName("李飞");
        student.setEmail("dunshan@qq.com");
        student.setAge(28);
        int nums = dao.insertStudent(student);
        sqlSession.commit();
        System.out.println("添加对象的数量:"+nums);
    }

再看一个例子
Student

public interface StudentDao {
    List<Student> selectStudentsByObject(Student student);
}

StudentDao.xml

    <select id="selectStudentsByObject" resultType="BUPT.entity.Student">
        select id, name, email, age
        from student
        where name=#{name} and id=#{id}
    </select>

Test

    @Test
    public void SelectByObject(){
        SqlSession sqlSession  = MyBatisUtils.getSqlSession();
        StudentDao dao  =  sqlSession.getMapper(StudentDao.class);

        Student student = new Student();
        student.setId(1008);
        student.setName("李飞");
        System.out.println("dao="+dao.getClass().getName());
        //调用dao的方法, 执行数据库的操作
        List<Student> students = dao.selectStudentsByObject(student);
        for(Student stu: students){
            System.out.println("学生="+stu);
        }
    }

按位置传值

按照StudentDao里面设置的接口参数的顺序
然后再StudentDao.xml中写配置文件时
写SQL语句时 where name=#{arg0} and age=#{arg1}
其中arg0代表第一个参数,arg1代表第二个
这样写可读性不是很强,不是很建议

Map传值

不常用,不建议
配置文件中写的是
where name=#{myname} and age=#{myage}
在测试方法中,建立map集合,其中的myname和myage都是map中的一个key
通过key找到对应map的value,传值给SQL语句
看着和传对象很像
比如数据写的是

Map<string,Object> data=new HashMap<>();
data.put("myname","zhangsan"};
data.put("myage",20};

就可以把data传过去

#号和$号

#:占位符,告诉 mybatis 使用实际的参数值代替。并使用 PrepareStatement 对象执行 sql 语句, #{…}代替sql 语句的“?”。 这样做更安全,更迅速,通常也是首选做法,防止SQL注入SQL注入参考博客
$ 字符串替换, 告诉 mybatis 使用$包含的“字符串”替换所在位置。使用 Statement 把 sql 语句和${}的
内容连接起来。主要用在替换表名,列名,不同列排序等操作
# 和 $区别

  1. #使用 ?在sql语句中做站位的, 使用PreparedStatement执行sql,效率高
  2. #能够避免sql注入,更安全。
  3. $不使用占位符,是字符串连接方式,使用Statement对象执行sql,效率低
  4. $有sql注入的风险,缺乏安全性。
  5. $:可以替换表名或者列名

定义别名

在mybatis主配置文件中定义,使用<typeAlias>定义别名
可以在resultType中使用自定义别名

之前在Student.xml中写的resultType都是全限定名称(当然推荐这样写)

<select id="selectStudentByTwoParams"  resultType="BUPT.entity.Student">
        select id, name, email, age
        from student
        where id = #{myid} and age=#{myage}
    </select>

可以在MyBatis.xml设置自定义名称

   <typeAliases>
        <typeAlias type="BUPT.entity.Student" alias="MyStu"></typeAlias>
    </typeAliases>

也可以写为
用package的方式,把包放进去,此时类名就是别名,不用写全限定名称

    <typeAliases>
              <package name="BUPT.entity"/>
    </typeAliases>

当然还是建议使用全限定名称,因为可能不同包下有同名的类

resultMap

resultMap:结果映射, 指定列名和java对象的属性对应关系。
1)你自定义列值赋值给哪个属性
2)当你的列名和属性名不一样时,一定使用resultMap

resultMap和resultType不要一起用,二选一

模糊查询

一是 java 代码中给查询数据加上“%” ; 二是在 mapper 文件 sql 语句的条件位置加上“%”

方法一:java 代码中提供要查询的 “李%”

接口方法:

public List<Student> selectLikeFirst(String name);

mapper 文件:

<select id="selectLikeFirst" resultType="BUPT.entity.Student">
select id,name,email,age from student
where name like #{studentName}
</select>

测试方法:

@Test
    public void testSelectLikeOne(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
        String name="李%";
        List<Student> stuList = dao.selectLikeFirst(name);
        stuList.forEach( stu -> System.out.println(stu));
    }

方法二:mapper 文件中使用 like name “%” #{xxx} “%”

接口方法:

public List<Student> selectLikeSecond(String name);

mapper 文件:

    <select id="selectLikeSecond" resultType="BUPT.entity.Student">
        select id,name,email,age from student
        where name like  #{studentName} "%"
    </select>

测试方法:

    @Test
    public void testSelectLikeSecond() {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
        String name = "李";
        List<Student> stuList = dao.selectLikeSecond(name);
        stuList.forEach(stu -> System.out.println(stu));
    }

结束时间:2021-12-08

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值