mybatis注解开发
@InsertProvider
@DeleteProvider
@UpdateProvider
@SelectProvider
以上四个注解用于使用SQL构建器来完成动态SQL的组建。
注解中有两个属性:type和method
type:用于构建SQL语句的类型的字节码对象
method:构建SQL语句的方法名称
案例:同时查询多个id值的用户对象
动态SQL的三种实现方式(注解实现动态sql)
方式一:自己拼接SQL语句
sql构建器
public class SqlBuilder {
public String getSelectUserSql(@Param("ids") List<Integer> ids){
String sql="select * from t_user where id in(";
for (int i = 0; i < ids.size(); i++) {
if(i<ids.size()-1) {
sql += ids.get(i) + ",";
}else {
sql += ids.get(i) + ")";
}
}
return sql;
}
}
mapper层
@SelectProvider(type =/*产生SQL语句的类的字节码对象*/SqlBuilder.class,method =/*产生SQL语句的方法名称*/"getSelectUserSql")
List<User> findByIds(@Param("ids") List<Integer> ids);
测试类
public static void main(String[] args) {
UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
List<User> users = mapper.findByIds(Arrays.asList(1, 2, 3, 4, 5));
users.forEach(user->{
System.out.println(user.getUsername());
});
}
方式二:使用mybatis提供的SQL类来完成动态SQL的创建
package com.woniuxy.utils;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.jdbc.SQL;
import java.util.List;
public class SqlBuilder {
public String getSelectUserSql(@Param("ids") List<Integer> ids){
//生成动态SQL的方式二
String condition="id in (";
for (int i = 0; i < ids.size(); i++) {
if(i<ids.size()-1) {
condition += ids.get(i) + ",";
}else {
condition += ids.get(i) + ")";
}
}
String sql = new SQL().SELECT("*")// SELECT *
.FROM("t_user")// from t_user
.WHERE(condition).toString();
return sql;
}
}
方式三:不使用provider,直接使用增删改查注解完成SQL语句的执行
注意:此处不再使用@SelectProvider,而是使用@Select注解完成动态SQL的执行,将XML中的动态SQL写到<script>标签中即可。
@Select("<script>" +
"select * from t_user where id in" +
" <foreach collection='ids' item='id' open='(' close=')' separator=','>" +
" #{id}" +
" </foreach>" +
"</script>")
List<User> findByIds(@Param("ids") List<Integer> ids);
代理模式
java:23种设计模式之一。
作用:为某个对象提供一种对象来控制对该对象访问。
代理模式的区别:主要是基于代理对象的生成时机来说的。
静态代理:在编译期,代理对象就已经生成了。
动态代理: 程序运行期间,动态生成代理对象。
不使用代理:
业务层接口
package com.woniuxy.service;
/**
* 业务层接口
*/
public interface UserService {
//新增
void add();
//删除
void delete();
//修改
void update();
//查询
void find();
}
业务层实现类
package com.woniuxy.service.impl;
import com.woniuxy.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("执行新增操作");
}
@Override
public void delete() {
System.out.println("执行删除操作");
}
@Override
public void update() {
System.out.println("执行修改操作");
System.out.println(1/0);
}
@Override
public void find() {
System.out.println("执行查询操作");
}
}
静态代理:
在编译期,代理对象就已经生成了。
package com.woniuxy.proxy.staticProxy;
import com.woniuxy.service.UserService;
import com.woniuxy.service.impl.UserServiceImpl;
/**
* 静态代理类
*/
public class StaticProxyObject implements UserService {
//在静态代理类中,创建真实对象的对象
private UserServiceImpl userServiceImpl=new UserServiceImpl();
@Override
public void add() {
System.out.println("开启事务");
try {
//调用真实对象方法
userServiceImpl.add();
System.out.println("提交事务");
} catch (Exception e) {
e.printStackTrace();
System.out.println("事务回滚");
}
}
@Override
public void delete() {
System.out.println("开启事务");
try {
userServiceImpl.delete();
System.out.println("提交事务");
} catch (Exception e) {
e.printStackTrace();
System.out.println("事务回滚");
}
}
@Override
public void update() {
System.out.println("开启事务");
try {
userServiceImpl.update();
System.out.println("提交事务");
} catch (Exception e) {
e.printStackTrace();
System.out.println("事务回滚");
}
}
@Override
public void find() {
}
}
测试
//创建静态代理对象
StaticProxyObject staticProxyObject = new StaticProxyObject();
//通过静态代理对象调用方法
staticProxyObject.add();
staticProxyObject.delete();
staticProxyObject.update();
使用静态代理的虽然可以消除业务层中非业务代码,让业务层保持纯粹性,但实现代理并没有减少,且每个代理类只能代理一个业务层实现类,反而增加了更多的类,因此一般没人用。
动态代理(重点):
在程序运行期间,动态生成代理对象。
动态代理的两种实现
JDK动态代理:
基于接口的实现。真实类型必须有接口,才可以使用JDK动态代理
要使用jdk动态代理,必须实现InvocationHandler接口,还需要使用Proxy类的相应API来完成代理对象的创建。
1、创建invocationHandler
/**
* jdk动态代理
*/
public class DynamicProxyUtil<T> implements InvocationHandler {
private T t;//真实对象
/**
* 获取代理对象
* @param real 真实对象
* @return
*/
public T getProxyObject(T real){
t=real;
//newProxyInstance(参数1,参数2,参数3)
//参数1:真实对象的类加载器
//参数2:真实对象所实现的接口数组
//参数3:invocationHandler
return (T)Proxy.newProxyInstance(real.getClass().getClassLoader(),
real.getClass().getInterfaces(),this);//用于生成代理对象
}
/**
*
* @param proxy
* @param method :method其实就是真实对象中某个方法的方法对象
* @param args :就是真实对象的某个方法的参数列表
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = null;
System.out.println("开启事务");
try {
returnValue = method.invoke(/*真实对象*/t, /*真实对象参数列表*/args);//调用了真实对象的方法
System.out.println("提交事务");
} catch (Exception e) {
e.printStackTrace();
System.out.println("事务回滚");
}
return returnValue;
}
}
2、测试
//创建代理对象
UserService userService = new DynamicProxyUtil<UserService>().getProxyObject(new UserServiceImpl());
//通过代理对象调用方法
userService.add();
userService.delete();
userService.update();
图示

CGLIB动态代理:
基于继承的实现,生成代理对象是以真实对象的子类的形式出现的。
实现步骤
1、导入 cglib-3.3.0.jar asm-7.1.jar
2、创建方法拦截器(创建类,实现MethodInterceptor接口)
/**
* cglib动态代理实现
*/
public class CglibDynamicProxyUtil implements MethodInterceptor {
/**
*
* @param clazz :真实对象的字节码对象
* @param <T>
* @return
*/
public <T>T getProxyObject(Class<T> clazz){
//使用Enhancer对象来完成代理对象的相关配置并创建代理对象
Enhancer enhancer = new Enhancer();
//设置代理对象的父类类型
enhancer.setSuperclass(clazz);
//设置方法拦截器
enhancer.setCallback(this);
//创建代理对象
return (T)enhancer.create();
}
/**
* @param obj :真实对象(业务层接口)
* @param method
* @param args :真实对象方法的参数列表
* @param methodProxy :真实对象某个方法的代理对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object returnValue =null;
System.out.println("开启事务");
try {
returnValue=methodProxy.invokeSuper(obj, args);
System.out.println("提交事务");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("事务回滚");
}
return returnValue;
}
}
3、测试
//创建代理对象
UserServiceImpl userServiceImpl = new CglibDynamicProxyUtil().getProxyObject(UserServiceImpl.class);
//通过代理对象调用方法
userServiceImpl.add();
userServiceImpl.delete();
userServiceImpl.update();
使用动态代理+自定义注解完成业务层指定方法的事务控制
案例:完成转账操作
自定义注解:如果业务层方法需要有事务,则在对应的方法上添加该注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional {
}
数据访问层
public interface AccountMapper {
/**
* 根据用户名查询账户信息
*/
@Select("select * from account where username=#{username}")
Account findByUsername(String username);
/**
* 根据用户名去修改账户余额
* @param account
* @return
*/
@Update("update account set balance=#{balance} where username=#{username}")
int updateAccount(Account account);
}
业务逻辑层
接口
public interface AccountService {
/**
* 执行转账的业务
* @return
*/
@Transactional
void transfer(String source,String target,Double money);
}
实现类:完成转账操作,需要查询和修改,会在业务层中调用数据访问层来实现对应的数据库操作
public class AccountServiceImpl implements AccountService {
private AccountMapper accountMapper;
public AccountServiceImpl(){
accountMapper= MybatisUtil.getMapper(AccountMapper.class);
}
/**
* 执行转账操作
* @param source
* @param target
* @param money
*/
@Override
public void transfer(String source, String target, Double money) {
//先查询
Account sourceAccount = accountMapper.findByUsername(source);//转出的账户
Account targetAccount = accountMapper.findByUsername(target);//转入的账户
if (sourceAccount.getBalance()>=money) {//如果余额大于要转出的金额,则执行转账操作
//后修改
sourceAccount.setBalance(sourceAccount.getBalance()-money);
targetAccount.setBalance(targetAccount.getBalance()+money);
accountMapper.updateAccount(sourceAccount);
System.out.println(1/0);
accountMapper.updateAccount(targetAccount);
}else{
throw new RuntimeException("余额不足");
}
}
}
动态代理工具类:在invoke()中,通过判断Method对象上是否存在对应的注解,来决定是否使用事务。
public class JdkDynamicProxyUtil<T> implements InvocationHandler {
private T t;
public T getProxyObject(T real){
t=real;
return (T)Proxy.newProxyInstance(real.getClass().getClassLoader(),
real.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue=null;
if (method.isAnnotationPresent(Transactional.class)) {
//真实对象的某个方法的方法对象上如果有对应的注解,则进行事务处理
try {
returnValue=method.invoke(t,args);
MybatisUtil.getSqlSession().commit();
} catch (Exception e) {
e.printStackTrace();
MybatisUtil.getSqlSession().rollback();
}
}else {//没有对应的注解,则不进行事务处理
returnValue=method.invoke(t,args);
}
return returnValue;
}
}
mybatis的工具类:该工具类中的Sqlsession应该确保是一个唯一的值。
public class MybatisUtil {
private static SqlSession sqlSession;
static {
//try with resources
try(InputStream is = Resources.getResourceAsStream("sqlConfig.xml");){
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
sqlSession = factory.openSession();
} catch (IOException e) {
e.printStackTrace();
}
}
public static <T>T getMapper(Class<T> clazz){
return sqlSession.getMapper(clazz);
}
public static SqlSession getSqlSession(){
return sqlSession;
}
}
测试
//创建代理对象
AccountService accountService = new JdkDynamicProxyUtil<AccountService>().getProxyObject(new AccountServiceImpl());
//通过代理对象调用方法
accountService.transfer("jack","tom",1000.00);
sqlSession = factory.openSession();
} catch (IOException e) {
e.printStackTrace();
}
}
public static <T>T getMapper(Class<T> clazz){
return sqlSession.getMapper(clazz);
}
public static SqlSession getSqlSession(){
return sqlSession;
}
}
测试
```java
//创建代理对象
AccountService accountService = new JdkDynamicProxyUtil<AccountService>().getProxyObject(new AccountServiceImpl());
//通过代理对象调用方法
accountService.transfer("jack","tom",1000.00);
本文介绍了Mybatis使用注解进行动态SQL构建的三种方式,包括自行拼接SQL、使用Mybatis SQL类以及直接应用增删改查注解。接着详细探讨了代理模式,包括静态代理和动态代理的差异,特别是JDK和CGLIB动态代理的实现。此外,还展示了如何结合自定义注解和动态代理实现业务层的事务控制。

被折叠的 条评论
为什么被折叠?



