本人正在浅学mybatis,正学到mybatis动态代理,在查询多方资料之后做出以下总结,以便于系统学习时回顾;
目录
MyBatis为什么引入动态代理
mybatis的动态代理
Dao代理技术
MyBatis为什么引入动态代理
因为程序员的 懒,为了节省代码的书写量,将书写复杂,没有一点子营养的机械代码交给java内置工具去写,下面使用案例解释;
一个完整的项目应该直接或间接拥有以下结构:
我们可以了解到里面有一个dao层的接口,还有dao层的实现dao.impl,之所以写这个接口,是因为在规范的实际开发中,写dao层的接口是编程规范,是必须写的。我们前期学习的时候会感觉dao层接口是没有实际作用的,也就是可以没有 ,但是实际开发中必不可少;下面是我前期的dao层代码;
编写AccountDao接⼝,以及AccountDaoImpl实现类
public interface AccountDao {
/**
* 根据账号获取账户信息
* @param actno 账号
* @return 账户信息
*/
Account selectByActno(String actno);
/**
* 更新账户信息
* @param act 账户信息
* @return 1表示更新成功,其他值表示失败
*/
int update(Account act);
}
package com.powernode.bank.dao.impl;
import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
public class AccountDaoImpl implements AccountDao {
@Override
public Account selectByActno(String actno) {
SqlSession sqlSession = SqlSessionUtil.openSession();
Account act = (Account)sqlSession.selectOne("selectByActno", actno
);
sqlSession.close();
return act;
}
@Override
public int update(Account act) {
SqlSession sqlSession = SqlSessionUtil.openSession();
int count = sqlSession.update("update", act);
sqlSession.commit();
sqlSession.close();
return count;
}
}
与之对应的sql映射文件,也就是mapper.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">
<mapper namespace="account">
<select id="selectByActno" resultType="com.powernode.bank.pojo.Accoun
t">
select * from t_act where actno = #{actno}
</select>
<update id="update">
update t_act set balance = #{balance} where actno = #{actno}
</update>
</mapper>
实际应用dao层以及dao的实现类:
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao = new AccountDaoImpl();
@Override
public void transfer(String fromActno, String toActno, double money) t
hrows MoneyNotEnoughException, AppException {
// 查询转出账户的余额
Account fromAct = accountDao.selectByActno(fromActno);
if (fromAct.getBalance() < money) {
throw new MoneyNotEnoughException("对不起,您的余额不⾜。");
}
try {
// 程序如果执⾏到这⾥说明余额充⾜
// 修改账户余额
Account toAct = accountDao.selectByActno(toActno);
fromAct.setBalance(fromAct.getBalance() - money);
toAct.setBalance(toAct.getBalance() + money);
// 更新数据库
accountDao.update(fromAct);
accountDao.update(toAct);
} catch (Exception e) {
throw new AppException("转账失败,未知原因!");
}
}
}
以上就是一个雏形的mybatis项目中的dao层的使用与代码书写;
上面的案例演示中只涵盖了一张表,然而当我们的项目中包含了很多张表,每一张表都对应一个mapper.xml文件,那就会需要多个dao层的接口,那我们是不是要写很多的实现类呢???数量到达千万级,岂不是要做很多重复的代码,枯燥无味。
于是前辈们就想能不能使用一个方法,让dao层的某一个接口默认直接和某一个mapper.xml文件相互映射对应;让dao层接口中的每一个方法都完全正确的对应mapper.xml文件中的一个SQL语句,这样我们就可以避免书写dao层的实现类,换句话说:就是让MyBatis框架给我们自动生成dao层接口的实现类。
mybatis的动态代理
上面的想法是可以实现的,称为MyBatis的动态代理。
employeeDao mapper = sqlSession.getMapper(employeeDao.class);
使⽤以上代码的前提是:AccountMapper.xml⽂件中的namespace必须和dao接⼝的全限定名称⼀致, id必须和dao接⼝中⽅法名⼀致。
<mapper namespace="com.node.dao.employeeDao">
<insert id="insert">
insert into employee
(id, name, age, position)
value
(#{id},#{name},#{age},#{position});
</insert>
dao接口:
public interface employeeDao {
/**
*@auther
*Description 插入数据
*@Date 15:08 2023/10/6
*@param emp
*@Return int
**/
int insert(employee emp);
}
当我们执行下面这条语句时:
employeeDao mapper = sqlSession.getMapper(employeeDao.class);
MyBatis框架底层是根据你的Dao接口,通过反射技术 在内存中虚拟的创建了dao接口的实现类对象。MyBatis把这个技术叫做 dao 的动态代理,或者是dao的代理技术;
然后我们就可以使用这个Mapper对象,对数据表进行增删改查:
employee employee = mapper.selectById(1);
Dao代理技术
由MyBatis创建Dao接口的实现类DaoImpl,使用这个框架创建的实现类来代替你自己写的实现类的功能;那么这样就不需要开发人员自己写实现类了;但是框架实际创建的并不是就叫这个名字, 框架创建的实现类的真正名字是 proxy。
使用MyBatis动态代理技术的前提:
AccountMapper.xml⽂件中的namespace必须和dao接⼝的全限定名称⼀致, id必须和dao接⼝中⽅法名⼀致。
在mapper.xml文件中,<mapper>标签有一个namespace属性,这个属性的属性值在整个项目中是惟一的。每一个SQL语句的标签,select,insert,delete等,都有一个id属性值,这个id属性值在这个mapper.xml文件中又是唯一的。这样根据这个字符串地址就能唯一确定一个SQL语句。
按照编码规范,namespace属性值必须是这个mapper.xml文件对应的dao层接口类的全限定路径名称。id属性值必须是这个sql 语句对应的dao层接口类中的方法名。
只有这样,MyBatis才能自动生成dao层接口对应的实现类。才能实现dao层接口的动态代理。
实际应用:
public class proxyTest {
@Test
public void test(){
SqlSession sqlSession = mybatisUtil.getSqlSession();
employeeDao mapper = sqlSession.getMapper(employeeDao.class);
employee employee = mapper.selectById(1);
System.out.println(employee);
}
}