1.Mybatis的动态sql介绍
如果读者你之前玩过Jdbc相关的框架,你可能就会明白sql拼接的无奈,然后拼接成变量,作为参数传入再查询等等。动态sql的出现就是为了解决这个拼接的问题。动态sql元素对于那些使用过JSTL或者XML文件处理器的人都十分熟悉的。而这同时也是Mybatis的强大功能之一。Mybatis使用了基于强大的OGNL表达式来消除了大部分元素。在我看来就是使用标签来减少sql语句的书写量。
2.都有哪些标签用于动态sql
if 、choose (when ,otherwise) 、trim(where,set)、foreach
3.详说各个标签
if标签:
if一看就大概知道是做条件判断的,如果我们不使用这个标签,我们肯定会在代码中判断如查询的元素是否为空,传入的元素是否为空,而这时我们直接使用这个标签,就减少了代码的书写。
choose标签:
对于这类标签,就是采用多个选项中找一个,就像单项选择题,但是你不会都选择,只会从中选择1个来作为条件。就有点类似于switch。。case。
trim标签:
如果使用第一个if语句的话,就会发现没有写where标签就会报错。而这类标签通常是搭配条件标签使用的。
foreach标签:
毫无疑问这个标签肯定是用于循环了,用于遍历,如果我们传入的参数是一个数组或者集合类,那么这个标签可以循环遍历。一般我们都是使用sql中的in语句时才使用。4.动态sql实现有条件的查询
总配置文件mybatis.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"></properties>
<typeAliases>
<typeAlias type="cn.spy.model.Student" alias="Student"/>
</typeAliases>
<environments default="mysql_developer">
<!-- mysql环境信息 -->
<environment id="mysql_developer">
<!-- mybatis使用jdbc事务管理方式 -->
<transactionManager type="jdbc"></transactionManager>
<!-- mybatis使用连接池方式来获取连接 -->
<dataSource type="pooled">
<!-- 配置与数据库交互的四个必要属性 -->
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="cn\spy\model\StudentMapper.xml"/>
</mappers>
</configuration>
db.propertis:
mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/basicjdbc?characterEncoding=utf-8
mysql.username=root
mysql.password=
工具类:为了以后可以方便使用
import java.io.IOException;
import java.io.Reader;
import java.sql.Connection;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
/*
* 工具类
* */
public class MybatisUtil {
private static ThreadLocal<SqlSession> threadLocal =new ThreadLocal<SqlSession>();
private static SqlSessionFactory sqlSessionFactory;
/*
* 加载位于src/mybatis.xml配置文件
* */
static{
try {
Reader reader =Resources.getResourceAsReader("mybatis.xml");
sqlSessionFactory =new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
}
}
//禁止外部访问
private MybatisUtil(){}
/*
* 获取SqlSession
* */
public static SqlSession getSqlSession(){
//从当前线程中获取sqlSession对象
SqlSession sqlSession =threadLocal.get();
if(sqlSession ==null){
//如果sqlSessionFactory对象不为空情况下,那么获取SqlSession对象。
sqlSession =sqlSessionFactory.openSession();
//将sqlSession与当前线程绑定在一起
threadLocal.set(sqlSession);
}
//直接返回sqlSession对象
return sqlSession;
}
/*
* 关闭SqlSession与当前线程分离
* */
public static void closeSqlSession(){
//从当前线程中获取sqlSession对象
SqlSession sqlSession =threadLocal.get();
if(sqlSession !=null){
//关闭sqlSession对象
sqlSession.close();
//分开当前线程与sqlSession对象的关系,目的是让GC尽早回收
threadLocal.remove();
}
}
public static void main(String[] args) {
Connection conn =MybatisUtil.getSqlSession().getConnection();
System.out.println(conn !=null ?"连接成功":"连接失败");
MybatisUtil.closeSqlSession();
}
}
model模型:
public class Student {
private Integer id;
private String name;
private double sal;
public Student(){}
public Student(Integer id, String name, double sal) {
this.id = id;
this.name = name;
this.sal = sal;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", sal=" + sal + "]";
}
}
StudentMapper.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="studentNamespace">
<resultMap type="Student" id="studentMap">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sal" column="sal"/>
</resultMap>
<select id="findConditionalStudent" parameterType="map" resultMap="studentMap">
select id,name,sal from students
<where>
<if test="pid !=null">
and id=#{pid}
</if>
<if test="pname !=null">
and name=#{pname}
</if>
<if test="psal !=null">
and sal=#{psal}
</if>
</where>
</select>
</mapper>
解释:可以发现上面使用了if和where标签,省去了我们在dao实现类中还得对于输入的字段进行非空判断,这样简化了我们的程序代码。
dao实现类:
public class StudentDaoImpl implements IStudentDao{
@Override
public List<Student> findConditionalStudent(Student student) throws Exception {
SqlSession sqlSession =null;
try{
sqlSession =MybatisUtil.getSqlSession();
Map<String,Object> map=new LinkedHashMap<String,Object>();
map.put("pid", student.getId());
map.put("pname", student.getName());
map.put("psal", student.getSal());
return sqlSession.selectList("studentNamespace.findConditionalStudent", map);
}catch(Exception e){
e.printStackTrace();
throw e;
}finally{
MybatisUtil.closeSqlSession();
}
}
}
5.动态sql实现更新学生表
至于配置文件和model类都不变,主要就是修改StudentMapper中的sql语句和dao实现类来操作sql。
StudentMapper.xml:
<update id="dynamicUpdate" parameterType="map">
update students
<set>
<if test="pname !=null">
name=#{pname},
</if>
<if test="psal !=null">
sal=#{psal},
</if>
</set>
where id=#{pid}
</update>
解释:可以说使用set和if标签实现了更新操作同时也做了非空判断。其实如果不使用这种标签,我们自己写的更新sql语句也与此类似。set XXX=#{xxx} ,YYY=#{yyy},只是非空判断需要在dao实现类做了。
dao实现类:
public class StudentDaoImpl implements IStudentDao{
@Override
public void dynamicUpdate(Integer id, String name, double sal)
throws Exception {
SqlSession sqlSession =null;
try{
sqlSession =MybatisUtil.getSqlSession();
Map<String,Object> map=new LinkedHashMap<String,Object>();
map.put("pid", id);
map.put("pname", name);
map.put("psal", sal);
sqlSession.update("studentNamespace.dynamicUpdate", map);
sqlSession.commit();
}catch(Exception e){
e.printStackTrace();
sqlSession.rollback();
throw e;
}finally{
MybatisUtil.closeSqlSession();
}
}
6.动态sql实现in语句批量删除记录
数组方式:
StudentMapper.xml:
<delete id="dynamicDelete">
delete from students where id in
<foreach collection="array" open="(" close=")" separator="," item="arr">
#{arr}
</foreach>
</delete>
解释:有人会问为什么没有输入参数parameterType,由于使用了foreach的collection属性已经指定了输入参数为array,所以就没必要再写了。foreach用于迭代数组元素,open表示开始符号,close表示结束符号,separator表示元素间的分割符,item表示迭代的数组。如果我们写sql语句:delete from students where id in (1,3,4,6,7...);而开始符号就是(,结束符号就是),分割符号是,item是我们指定的迭代数组。
dao实现类:
public void dynamicDelete(Integer[] arr) throws Exception {
SqlSession sqlSession =null;
try{
sqlSession =MybatisUtil.getSqlSession();
sqlSession.delete("studentNamespace.dynamicDelete", arr);
sqlSession.commit();
}catch(Exception e){
e.printStackTrace();
sqlSession.rollback();
throw e;
}finally{
MybatisUtil.closeSqlSession();
}
}
集合类方式:
StudentMapper.xml:
就是使用List作为输入参数,与上面的数组是类似 的,只是指定输入参数时为list。
<delete id="dynamicDeleteList">
delete from students where id in
<foreach collection="list" open="(" close=")" separator="," item="ids">
#{ids}
</foreach>
</delete>
dao实现类:
@Override
public void dynamicDeleteList(List<Integer> ids) throws Exception {
SqlSession sqlSession =null;
try{
sqlSession =MybatisUtil.getSqlSession();
sqlSession.delete("studentNamespace.dynamicDeleteList", ids);
sqlSession.commit();
}catch(Exception e){
e.printStackTrace();
sqlSession.rollback();
throw e;
}finally{
MybatisUtil.closeSqlSession();
}
}
结果:
7.动态sql实现插入数据
StudentMapper.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="studentNamespace">
<resultMap type="Student" id="studentMap">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sal" column="sal"/>
</resultMap>
<sql id="key">
<trim suffixOverrides=",">
<if test="id !=null">
id,
</if>
<if test="name !=null">
name,
</if>
<if test="sal !=null">
sal,
</if>
</trim>
</sql>
<sql id="value">
<trim suffixOverrides=",">
<if test="id !=null">
#{id},
</if>
<if test="name !=null">
#{name},
</if>
<if test="sal !=null">
#{sal},
</if>
</trim>
</sql>
<insert id="dynamicInsert" parameterType="Student">
insert into students(<include refid="key"/>) values(<include refid="value"/>);
</insert>
</mapper>
解释:如果我不使用动态sql语句写的话是insert into students(id,name,sal) values(#{id},#{name},#{sal});这种形式而此处使用动态sql语句就是把后面参数的传递使用动态sql实现。后面分为两组参数,一组是传入哪些字段参数,另一组是传入的字段参数值。定义了两个sql片段,形式就是<sql></sql>.
由于需要判断参数是否为空,则还需要前面提到的if标签来做判断,如果我们不设置<trim></trim>标签就提交的话,那么程序会报sql使用错误。我们会发现其实最后一个参数的结尾是不应该有逗号的,那么我们就需要去掉,可以使用标签<trim></trim>,用来将逗号去掉换成空格。使用suffixOverrides属性设置要去掉的符号,从属性名的意思也可以看出是去掉最后的参数的。而<sql>片段是通过<include>标签使用的。
dao实现类:
@Override
public void dynamicInsert(Student student) throws Exception {
// TODO Auto-generated method stub
SqlSession sqlSession =null;
try{
sqlSession =MybatisUtil.getSqlSession();
sqlSession.insert("studentNamespace.dynamicInsert", student);
sqlSession.commit();
}catch(Exception e){
e.printStackTrace();
sqlSession.rollback();
throw e;
}finally{
MybatisUtil.closeSqlSession();
}
}
测试:
public static void main(String[] args) throws Exception {
IStudentDao studentDao =new StudentDaoImpl();
studentDao.dynamicInsert(new Student(14,null,3000D));
}
结果:
----------------------------------------------------