动态SQL
采用OGNL表达式来进行SQL的淘汰不需要的元素。根据不同的添加进行动态的SQL的组装和拼接
if关键字
if标签:包含test表达式 必填,通过ognl表达式进行判断,true和false,
如果为真,会执行if中的SQL
如果为假,则不执行if中的SQL
where 标签
where标签作用:如果where标签包含了具有返回值时,就会插入一个where,如果where后面的字符串以and 和or开头的,则将他剔除
where和if联合使用
where表达式一般和if表达式一块使用,如果一个条件都不满足,则不拼接where到SQL中,如果有一个或者多个if表达式成立,where会直接拼接在SQL上,并且紧随where的表达式的and或者or会被忽略
<select id="selectClass" resultType="org.example.pojo.Class">
select * from
<where>
<if test="c_id!=null">
and c_id=#{c_id}
</if>
<if test="c_name!=c_name">
and c_name=#{c_name}
</if>
</where>
如果传入c_id则SQL语句为: select * from wherec_id=#{c_id}
两个都传入则SQL语句为:select * from c_id=#{c_id}
set标签
set标签的作用,如果set标签中的元素有返回值,就插入一个set,如果set后的字符串以逗号为结尾,则将最后的逗号剔除
set标签和if表达式一块使用,如果有条件满足时,会直接拼接一个set到SQL上,并将最后一个SQL的逗号去掉
<update id="updateStudent" parameterType="student" >
update student
<set>
<if test="Sname != null ">
Sname = #{Sname} ,
</if>
<if test="Sage != null ">
Sage = #{Sage} ,
</if>
</set>
where SID = #{SID}
</update>
trim标签
trim标签使用来去除SQL语句中多余的and关键字、逗号,或者给SQL拼接的where、set或者values等前后缀,可以选择性的进行插入、更显,或者添加查询操作
*prefix:前缀
prefixoverride:去掉第一个and或者是or
suffix:后缀
suffixoverride:去掉最后一个逗号(也可以是其他的标记,就像是上面前缀中的and一样)
<select id="selectStudentByDIY" parameterType="student" resultType="student">
select * from Student
<trim prefix="where" prefixOverrides="and">
<if test="SID != null and SID != 0">
and SID = #{SID}
</if>
<if test="Sname != null ">
and Sname = #{Sname}
</if>
</trim>
</select>
<update id="updateStudent" parameterType="student" >
update student
<trim prefix="set" suffixOverrides=",">
<if test="Sname != null ">
Sname = #{Sname} ,
</if>
<if test="Sage != null ">
Sage = #{Sage} ,
</if>
</trim>
where SID = #{SID}
</update>
foreach标签
批量处理
如:
insert table_name (id,name,age) values((1,1,1),(2,2,2))
select * from Student where SID in(1,2,3,4,5);
foreach标签实现批量数据处理,遍历集合对象
各属性解释
collection:必填,指定输入参数类型(列表:list 数组:array hashmap:map)
item:起名字,给定集合中单个元素的名称
open:开始的字符串
close:结束的字符串
separator:数据之间的分隔符
index:索引的属性名,当类型为数组时是当前的索引值
<select id="selectStudentByIds" resultType="student">
select * from student where SID in
<foreach collection="list" item="id" open="(" close=")" separator="," index="">
#{id}
</foreach>
</select>
动态代理
代理模式是设计模式之一
委托类(RealSubject):真正实现功能或者提供真实服务
代理类(Proxy):并不提供真正实现服务,而是通过调用委托类的对象的相关方法提供特定服务,可以在该服务基础上提供一些其他的服务
subject:代理类和委托类共有的接口或者服务,提供了代理类和委托类共有方法
Client需要的服务是是通过代理类来获取的,而真正的服务是有委托类提供的
JDK自带动态代理
接口:
/**
* 代理类和委托类共有的接口
*/
public interface Iuser {
void eate();
}
委托类:
/**
* 委托类:具体实现接口中方法
*/
public class User implements Iuser{
@Override
public void eate() {
System.out.println("User实现了eate方法");
}
}
代理辅助类:
/**
* 代理辅助类
* 要实现动态代理,则需要给定一个实现类invocationHandler接口的实现类
* 该类中需要持有IUser实现类的引用
*/
public class UserProxy implements InvocationHandler {
private Iuser user;
public UserProxy(Iuser user){
this.user=user;
}
/**
* 实现InvocationHandler接口
* 重写invoke方法
* @param proxy :产生的代理对象
* @param method:调用的方法
* @param args :表示方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理类自定义业务逻辑1");
//调用委托类的方法
method.invoke(user,args);
System.out.println("代理类自定义业务逻辑2");
return null;
}
产生代理类的形式:
public static void main(String[] args) {
/**
* ClassLoader loader,指定代理辅助类的加载器
* Class<?>[] interfaces,当前被代理的接口
* InvocationHandler h :代理辅助类
*/
//产生的代理类
Iuser user = (Iuser) Proxy.newProxyInstance(UserProxy.class.getClassLoader(), new Class[]{Iuser.class}, new UserProxy(new User()));
user.eate();
}
运行结果:
通过分析:动态打理的对象的产生通过Proxy.newProxyInstance()调用产生了代理对象
代理对象调用eate方法,通过打印结果可知,其调用了invocationHandler辅助类的invoke方法,该方法下来调用委托类User的eate实现
CGLib代理
JDK自带的动态代理使用比较简单,缺点也是明显的,需要目标对象实现一个或多个接口
当代理没有接口的类,此时Proxy和InvocationHandler机制就不能使用了,此时就需要使用cglib
cglib采用字节码技术,通过对字节码为一个类创建子类,并在子类中采用方法拦截的技术,拦截所有父类方法的调用,顺势织入横切技术
AOP技术的实现是使用到了JDK自带的动态代理和CGLib动态代理的
演示
引入依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.2</version>
</dependency>
委托类:
public class CCGlibSuper {
public void doing(){
System.out.println("委托类");
}
}
辅助类:
/**
* 产生CGlib代理对象的辅助类
* 实现MethodInterceptor
* 该接口中的intercept方法需要实现
*/
public class CGLibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public <T> T getProxy(Class<T> tClass){
//设置父类
enhancer.setSuperclass(tClass);
enhancer.setCallback(this);
//通过字节码技术创建子类实例
return (T)enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理类使用前");
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("代理类使用后");
return o1;
}
public static void main(String[] args) {
CGLibProxy cgLibProxy = new CGLibProxy();
CCGlibSuper proxy = cgLibProxy.getProxy(CCGlibSuper.class);
proxy.doing();
}
}
MyBatis中的动态代理
MyBatis中使用的是 JDK自带动态代理模式
添加接口信息如下
configuration.addMapper(StudentMapper.class);//添加mapper
addMapper方法实现:
Configuration类:
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
mapper实际上被添加到了mapperRegistry中
MapperRegistry类:
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) { //只添加接口
if (hasMapper(type)) {//不能重复添加
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
mybatis在执行configuration.addMapper(StudentMapper.class),最终将接口信息放到HashMap中,名称为knownMappers,knownMappers是MapperRegistry类的私有属性,是一个HashMap,key为当前class对象,value为一个MapperProxyFactory类型的实例
继续看getMapper的代码跟踪
//通过动态代理机制(反射)产生了一个代理类(重点:动态代理模式,源码实现)
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
getMapper实现类通过debug测试确定为DefaultSQLSession类
DefaultSQLSession类:
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
该代码直接调用Configuration类中的getMapper方法
Configuration类:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
通过代码是通过Proxy.newProxyInstance产生了一个StudentMapper接口的代理对象,mybatis为了完成mapper接口的实现运用了代理模式