1、什么是Mybatis?与hibernate的区别?
Mybatis是一种半ORM框架,因为它还需要在应用程序中编写sql语句,而不像hibernate可以直接操作实体对象。
Mybatis无法做到数据库无关性,若要支持各种数据库,则需要自定义sql映射文件,而hibernate数据库无关性好
Mybatis主要有两部分:
一个mapper接口文件和一个xml映射文件,首先XML文件的sql语句会被解析为为MapperStatement对象,然后使用动态代理为mapper接口生成代理对象。代理对象对接口进行增强,并通过接口全限名+方法名作为key,找到对应的MapperStatement对象执行sql
2、Mybatis优缺点
一、优点
(1)灵活
(2)sql语句与程序代码解偶
(3)sql复用
二、缺点
(1)数据库移植性差
适用于性能高变化多的项目
3、#{}和${}区别
#{}可以将sql中的?进行替换,防止sql注入,同时还可以自动转化字段类型
${}只会解析注入的变量,保持原数据类型并不会自动转化,有慢sql风险
4、分页
(1)手动在代码里将返回结果分页
(2)在sql中通过limit分页
(3)github的PageHelper插件分页或自定义插件
5、一对多和多对一查询
一对多:resultMap设置collection节点
多对一:resultMap设置association节点
6、一级和二级缓存
(1)一级缓存(默认开启)
一级缓存是sqlsession级别的缓存,也就是不同的查询sql有各自不同的缓存,基于HashMap存储
如果有增删改操作,则会情况缓存,避免脏读
Spring结合mybatis后,有如下两种情况:
一、未开启事务的情况下
每次查询,Spring都会关闭旧的sqlsession,同时创建新的sqlsession,所以一级缓存会失效
二、在开启事务的情况下
Spring会吧sqlsession存在threadlocal中,因此同一个线程中,一级缓存是有效的
(2)二级缓存(默认关闭)
二级缓存是mapper级别的缓存,每一个namespace的mapper都有一个二级缓存区,也是基于HashMap存储
如果开启二级缓存,关闭sqlsession后,会把数据添加到二级缓存中,从而让缓存生效
但如果存在不同的namespace的mapper对同一个表进行操作,则会因为缓存脏数据,导致某一个mapper中的查询过时,所以二级缓存是默认关闭的
7、事务传播行为(propugation)
(1)required,表示当前方法必须运行在事务中,如果当前事务存在,方法将会在该事务中运行,否则会启动一个新的事物
(2)supports,表示当前方法不需要事物上下文,但如果存在当前事务的话,那么该方法会在这个事务中运行
Spring的事务原理:AOP + ThreadLocal(持有数据库connection),底层还是使用的数据库事务
8、代理模式
代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能(比如加日志)。
简言之,代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式。
静态代理
这种代理方式需要代理对象和目标对象实现一样的接口,代理对象持有目标对象引用
优点:可以在不修改目标对象的前提下扩展目标对象的功能。
缺点:
冗余--由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
不易维护--如果接口增加方法,则目标对象与代理对象都要进行修改。
比如UserDaoProxy是UserDao的代理类,持有UserDao的引用
public interface IUserDao {
public void save();
}
class UserDao implements IUserDao{
@Override
public void save() {
System.out.println("保存数据");
}
}
public class UserDaoLogProxy implements IUserDao{
private IUserDao target;
public UserDaoProxy(IUserDao target) {
this.target = target;
}
@Override
public void save() {
System.out.println("方法执行前打印日志");//扩展了额外功能
target.save();
System.out.println("方法执行后打印日志");
}
}
public static void main(String[] args) {
//目标对象
IUserDao target = new UserDao();
//代理对象
UserDaoLogProxy proxy = new UserDaoProxy(target);
proxy.save();
}
动态代理
动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。
动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法,并利用Proxy.newProxyInstance返回一个代理类,比如一个日志代理类
public class UserDaoLogHandler implements InvocationHandler {
// 目标对象
private Object targetObject;
//绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
public Object getProxyInstance(Object targetObject){
this.targetObject=targetObject;
//该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
//第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
//第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
//第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
//根据传入的目标返回一个代理对象
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),this);
}
@Override
//关联的这个实现类的方法被调用时将被执行
/*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object ret=null;
try{
System.out.println("方法执行前打印日志");
//调用目标方法
ret=method.invoke(targetObject, args);
System.out.println("方法执行后打印日志");
}catch(Exception e){
e.printStackTrace();
System.out.println("error-->>");
throw e;
}
return ret;
}
}
public static void main(String[] args) {
//目标对象
IUserDao target = new UserDao();
//代理对象
IUserDao proxy = (IUserDao) new UserDaoLogHandler().getProxyInstance(target);
proxy.save();
}
cglib代理
cglib (Code Generation Library )是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。
特点
(1)JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。
(2)CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。
(3)CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉。
cglib与动态代理最大的区别就是
(1)使用动态代理的对象必须实现一个或多个接口
(2)使用cglib代理的对象则无需实现接口,达到代理类无侵入。
如下所示,创建一个ciglib代理类需要实现MethodInterceptor接口,并重新intercept方法实现增强
public class UserDaoLogProxy implements MethodInterceptor{
private Object target;//维护一个目标对象
public ProxyFactory(Object target) {
this.target = target;
}
//为目标对象生成代理对象
public Object getProxyInstance() {
//工具类
Enhancer en = new Enhancer();
//设置父类
en.setSuperclass(target.getClass());
//设置回调函数
en.setCallback(this);
//创建子类对象代理
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("执行方法前打印日志");
// 执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("执行方法后打印日志");
return returnValue;
}
}
public static void main(String[] args) {
//目标对象
IUserDao target = new UserDao();
//代理对象
IUserDao proxy = (IUserDao) new UserDaoLogProxy().getProxyInstance(target);
proxy.save();
}