动态SQL
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
动态SQL就是根据不同的条件拼接SQL语句
提示:以下是本篇文章正文内容,下面案例可供参考
一、动态SQL中常用元素
1. if元素
用于单条件判断
在实际开发过程中,我们可能需要通过某个条件查询某个对象的信息,就需要通过if条件判断
<select id = "findCustomerByNameAndJobs" //通过姓名和职业查询信息
parameterType = "com.itiheima.pojo.Customer"//接收的参数类型为对象
resultType = "com.itheima.pojo.Customer">//返回的参数也为对象
select * from t_customer where 1=1/*为了拼接有效的条件,这里用一个永真的条件,
这样后面是真的,就会是select * from t_costomer where 1=1 and useanme ...而不是where
and...这样的错误语句*/
<if test = "username != null and username != ''">//test属性是用于对username进行非空判断
and username like concat('%',#{username},'%')//如果条件为真,这条语句就会拼接到查询语句中
</if>
<if test = "jobs != null and jobs != ''">
and jobs = #{jobs}//#{jobs}是一个占位符,用于接收之后传来的对象中的属性
</if>
</select>
编写类方法进行测试
@Test
pubilc void findCustomerByNameAndJobsTest(){
SqlSession session = MyBatisUtis.getSession();//引入之前建好的工具类中的方法创建SqlSession
Customer customer = new Customer();
customer.setUsername("Jack");//传入要查询对象的名字
customer.setJobs("teacher");//传入想要查询对象的职业名称
//执行SqlSession的查询方法,返回结果集
List<Customer> customers = session.selectList("com.itheima.mapper"+".CustomerMapper.findCustomerByNameAndJobs",customer);
/*
这里传入的是映射文件中查询方法的全限定类名和已经赋值的costomer对象
*/
//输出结果集
......
}
2. chose、when、otherwise元素
chose、when、otherwise联合起来相当于java语句中的switch…case…default语句,用于多条件判断,但是最终只会执行其中的一个。
<select id = "findCustomerByNameAndJobs" //通过姓名和职业查询信息
parameterType = "com.itiheima.pojo.Customer"//接收的参数类型为对象
resultType = "com.itheima.pojo.Customer">//返回的参数也为对象
select * from t_customer where 1=1
<choose>
<when test = "username != null and username != ''">
and username like concat('%',#{username},'%')//模糊查询
</when>
<when test = "jobs != null and jobs != ''">
and jobs = #{jobs};
</when>
<otherwise>
and phone is not null
</otherwise>
</select>
上述代码中,如果第一个条件为真,则则执行第一个,后面不执行;否则判断第二个,如果真,只执行第二个;如果所有的when元素都不为真,则就会默认拼接default中的语句并执行。不管怎样,总会执行一条语句,不会所有的条件都不执行。
3. where、trim元素
(1)where元素会替换掉原来的select * from users where 1=1中的where 1=1,并且会自动将where之后的“AND(and)”或“OR(or)”去掉
<select id = "findCustomerByNameAndJobs" //通过姓名和职业查询信息
parameterType = "com.itiheima.pojo.Customer"//接收的参数类型为对象
resultType = "com.itheima.pojo.Customer">//返回的参数也为对象
select * from t_customer
<where>
<if test = "username != null and username != ''">
and username like concat('%',#{username},'%')
</if>
<if test = "jobs != null and jobs != ''">
and jobs = #{jobs}
</if>
</where>
</select>
注意:只有<where>元素内的某一个或多个条件成立时,才会进行拼接并且加入关键字“where”,否则不会添加
(2)trim元素用于删除多余的关键字,可以直接实现where元素的功能。包含四个属性:prefix、prefixOverrides、suffix、suffixOverrides。
<select id = "findCustomerByNameAndJobs" //通过姓名和职业查询信息
parameterType = "com.itiheima.pojo.Customer"//接收的参数类型为对象
resultType = "com.itheima.pojo.Customer">//返回的参数也为对象
select * from t_customer
<trim prefix = "where" prefixOverrides= "and">//为SQL语句增加前缀“where”,去除指定的前缀“and”
<if test = "username != null and username != '' ">
and username like concat('%',#{username},'%')
</if>
<if test = "jobs != null and jobs != '' ">
and jobs = #{jobs}
</if>
</trim>
</select>
suffix元素是指给SQL语句增加的后缀;suffixOverrides是指指定SQL语句中去掉的后缀字符串。
4.set元素
set元素用于更新操作,了、它可以在动态SQL语句前输出一个关键字SET,并将SQL语句最后一个多余逗号去掉。使用set元素与if元素相结合的方式可以更新需要更新的字段。
<update id = "updateCustomer" parameterType = "com.itheima.pojo.Customer">
update t_customer
<set>
<if test = "username != null and username != '' ">
username = #{username},
</if>//如果传入为空则不拼接
<if test = "jobs != null and jobs != '' ">
jobs = #{jobs},
</if>//传入为空则不拼接
<if test = "telephone != null and telephone != '' ">
telephone = #{telephone},
</if>//传入为空则不拼接
注意:如果<set>元素内包含的内容都为空,则会出现SQL语法错误。因此使用<set>元素进行字段信息更新时,要确保传入的更新字段不能都为空。
更新除了可以使用set元素外,还可以使用trim元素。
5. foreach元素
foreach元素主要用于遍历,能够支持数组、List或Set接口的集合。通常和关键字“in”结合使用。
(1)item:表示集合中每一个元素进行迭代时的别名。该属性为必选属性。
(2)index:在List和数组中,index是元素的序号;在Map中index是元素的key。该属性为可选项。
(3)open:表示foreach语句代码开始的符号,一般和cllose=“)”合用。常用在in条件语句中。该属性为可选属性。
(4)separator:表示元素之间的分隔符。
(5)close:foreach语句代码的关闭符号。
(6)collection:必选项。若传入参数为单参数且是一个List,属性值为list,若为数组,属性值为array;若为多参数,封装成一个Map,属性值为Map。
二、总结
对所学知识的一个笔记!!!
(一)Mybatis的特点:
1.Mybatis是面向接口的开发,只需要写接口,不需要自己进行实现类的书写
2.实现接口通过一个一个Mapper文件进行映射,每一个接口中的方法要和Mapper文件中对应实现的sql语句的id值对应
3.namespace是实现接口的接口名,每个标签中的id是接口中的方法
4.Mybatis中事务的自动提交关闭了,所以需要自动进行提交
(二)Mybatis使用的两种方式
1.不创建接口
(1)创建Mapper.xml文件进行sql语句的统一管理
(2)增删改查语句通过SqlSession进行调用,通过mapper.xml中的id值作为方法得到参数进行对应语句的执行
(3)参数注入进行数据库的操作
参数注入使用#{}或者${}:
- 如果调用的方法进行参数注入时,参数只有一个,{}可以写任意内容
- 如果调用的方法进行参数注入时,参数有多个,{}中写的是map的key
- 如果调用的方法进行参数注入时,此时注入一个对象,对象的属性默认作为map的key
2.使用接口
(1)mapper.xml对应接口,进行方法调用
(2)参数注入时 - 如果一个参数,{}参数注入进行任意书写
- 如果多个参数,{}写map的key,key需要在接口中的方法进行@Param注解定义
- 如果参数是一个对象,对象的属性作为key,进行参数注入
3.要在带注解的映射器接口中使用动态SQL,可以使用script元素,例如
要在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素。比如:
@Update({"<script>",
"update Author",
" <set>",
" <if test='username != null'>username=#{username},</if>",
" <if test='password != null'>password=#{password},</if>",
" <if test='email != null'>email=#{email},</if>",
" <if test='bio != null'>bio=#{bio}</if>",
" </set>",
"where id=#{id}",
"</script>"})
void updateAuthorValues(Author author);
(三)作用域和生命周期
1.依赖注入框架可以创建线程安全的、基于事务的SqlSession和映射器,并将它们直接注入到bean中,因此可以直接忽略它们的生命周期。 如果对如何通过依赖注入框架使用 MyBatis 感兴趣,可以研究一下 MyBatis-Spring 或 MyBatis-Guice 两个子项目。
2.Mybatis三大对象
- SqlSessionFactoryBuilder:这个类可以被实例化、使用和丢弃,一旦创建了SqlSessionFactory,就不再需要SqlSessionFactoryBuilder,因此它的最佳作用域是方法作用域(局部方法变量),可以重用SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好不要一直保留,以保证所有XML解析资源可以被释放到更重要的事情
- SqlSessionFactory:一旦被创建就应该在运行的期间一直存在,没有任何理由丢弃或重新创建另一个实例;使用SqlSessionFactory最佳实践是在应用期间不要重复创建多次,多次创建被视为一种代码的“坏习惯”,最佳作用域是应用作用域,最简单就是使用单例模式或者静态单例模式
- SqlSession:没给线程都应该有它自己的SqlSession实例,但不是线程安全的,不能被共享;因此最佳作用域是请求或方法作用域;不能将SqlSession实例的引用放在一个类的静态域甚至是一个类的实例变量,也决不能将 SqlSession 实例的引用放在任何类型的托管作用域中,如Servlet框架中的HttpSession,在web中考虑放到一个和Http请求相似的作用域;即每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,应该把这个关闭操作放到 finally 块中。
(四)Mybatis缓存
1.一级缓存:用SqlSession进行数据库中数据的查询,默认开启,一级缓存数据存在于SqlSession中
(1)一级缓存用的是同一个SqlSession,并且只有方法名,参数都相同才会使用缓存,否则还是会访问数据库
(2)map结构进行的数据存储
2.二级缓存:手动开启,而且进行结果集对应的类,进行序列化;namespace的缓存
<mapper namespace="com.neuedu.dao.UserDao">
<cache/>
不共用SqlSession但是相同方法,相同参数会从缓存中去取,不会再访问数据库
(五)resultMap的标签
作用:
(1)表中的列名和java中实体类属性名不一致时可以进行对应
进行列名和属性名的对应
<resultMap id="users" type="User">
<id column="id" property="id" />
<result column="r_id" property="rId"/>
</resultMap>
<select id="gets" resultMap="users">
-- select id,username,password,email,phone,r_id from user
select * from user
</select>
(2)在一对多,多对一时使用ResultMap
进行一对多的配置
public class Role {
private Integer id;
private String name;
// 一个角色可以对应多个用户,一对多的关系
private List<User> users;
}
<resultMap id="roles" type="Role">
<collection property="users" select="getById" column="id">
<result property="rId" column="r_id"/>
</collection>
</resultMap>
<resultMap id="users" type="User">
<result column="r_id" property="rId"/>
</resultMap>
<select id="getAll" resultMap="roles">
select * from role
</select>
<select id="getById" resultMap="users">
select * from user where r_id=#{id}
</select>
@Test
public void test27(){
SqlSession sqlSession = factory.openSession();
RoleDao mapper = sqlSession.getMapper(RoleDao.class);
List<Role> mapper2 = mapper.getAll();
System.out.println(mapper2);
sqlSession.commit();
}
进行表连接的语句书写
进行所有列的对应:
<resultMap id="roles" type="Role">
<id property="id" column="id"/>
<result property="name" column="name"/>
<collection property="users" javaType="list" ofType="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="email" column="email"/>
<result property="password" column="password"/>
<result property="phone" column="phone"/>
<result property="rId" column="r_id"/>
</collection>
</resultMap>
<select id="getAll" resultMap="roles">
select * from role,user where role.id=user.r_id
</select>
(六)动态代理
对象的执行方法交给代理(proxy)来负责。
接口定义,进行了方法声明:
(1)方法调用,接口进行了mapper接收,调用方法,进行方法功能的实现
(2)接口的实现类对象,动态代理
举例子:
静态代理:公司常年法务–》静态代理
动态代理:个人律师–》代理完就没事了,代理关系解除
1.JDK动态代理:要求有接口
接口:
public interface Core {
void t1();// 核心要做的一件事 打官司
}
接口实现类:
public class CoreImpl implements Core {
@Override
public void t1() {
System.out.println("打官司");
}
}
public class Test1 implements InvocationHandler//代理类 {
private Core core;
public Test1(Core core){
this.core=core;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("请律师");
method.invoke(core,args);
System.out.println("合同接触==============");
return null;
}
}
public class Demo {
public static void main(String[] args) {
Core core = new CoreImpl();
Test1 t1 = new Test1(core);
Core o = (Core)Proxy.newProxyInstance(Core.class.getClassLoader(), CoreImpl.class.getInterfaces(), t1);//产生代理对象
o.t1();
Core c1 = new CoreImpl();
c1.t1();
}
}
2.CGLib动态代理
需要导包
public class CCore {父类的创建
public void t1(){
System.out.println("核心功能执行");
}
}
代理逻辑
package com.test;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前期请律师");
methodProxy.invokeSuper(o,objects);
System.out.println("后期进行合同解除");
return null;
}
}
代理实现
public class CDemo {
public static void main(String[] args) {
Enhancer e = new Enhancer();
e.setSuperclass(CCore.class);
e.setCallback(new CProxy());
CCore o = (CCore)e.create();
o.t1();
CCore c = new CCore();
c.t1();
}
}
(七)单例模式
一个类只有一个对象,节省内存,节省对象创建和销毁占用的时间
1.饿汉模式
为静态变量进行赋值,只要类加载了,对象已经存在,不管是否使用,对象已经占用了内存
public class Cat {
private String name;
private static Cat cat=new Cat(); 静态的全局变量
// 构造方法要私有
private Cat(){ 构造方法私有
}
public static Cat getOne(){ 方法进行对象的返回
return cat;
}
}
2.懒汉模式
类加载时,对象不进行创建,什么时候用,什么时候进行对象的创建
public class Cat {
private String name;
private static Cat cat;
// 构造方法要私有
private Cat(){
}
public static Cat getOne(){
if (cat==null){
cat = new Cat();
}
return cat;
}
}