Mybatis学习记录

Mybatis学习记录

一 MyBatis简介与配置 MyBatis+Spring+MySql

1.1 MyBatis简介

MyBatis 是一个可以自定义SQL、存储过程和高级映射的持久层框架。MyBatis 摒除了大部分的JDBC代码、手工设置参数和结果集重获。MyBatis 只使用简单的XML 和注解来配置和映射基本数据类型、Map 接口和POJO 到数据库记录。相对Hibernate和Apache OJB等“一站式”ORM解决方案而言,Mybatis 是一种“半自动化”的ORM实现。
需要使用的Jar包:mybatis-3.0.2.jar(mybatis核心包)。mybatis-spring-1.0.0.jar(与Spring结合包)。

下载地址:
http://ibatis.apache.org/tools/ibator/
http://code.google.com/p/mybatis/

1.2 MyBatis+Spring+MySql简单配置

1.2.1 搭建Spring环境

  1. 建立maven的web项目;

  2. 加入Spring框架、配置文件;

  3. 在pom.xml中加入所需要的jar包(spring框架的、mybatis、mybatis-spring、junit等);

  4. 更改web.xml和spring的配置文件;

  5. 添加一个jsp页面和对应的Controller;

  6. 测试。

可参照:使用Eclipse的Maven构建SpringMVC项目

1.2.2 建立MySql数据库

建立一个学生选课管理数据库。
表:学生表、班级表、教师表、课程表、学生选课表。
逻辑关系:每个学生有一个班级;每个班级对应一个班主任教师;每个教师只能当一个班的班主任;

使用下面的sql进行建数据库,先建立学生表,插入数据(2条以上)。

更多sql请下载项目源文件,在resource/sql中。

/* 建立数据库 */  
CREATE DATABASE STUDENT_MANAGER;  
USE STUDENT_MANAGER;  

/***** 建立student表 *****/  
CREATE TABLE STUDENT_TBL  
(  
    STUDENT_ID         VARCHAR(255) PRIMARY KEY,  
    STUDENT_NAME       VARCHAR(10) NOT NULL,  
    STUDENT_SEX        VARCHAR(10),  
    STUDENT_BIRTHDAY   DATE,  
    CLASS_ID           VARCHAR(255)  
);  

/*插入学生数据*/  
INSERT INTO STUDENT_TBL (STUDENT_ID,  
                         STUDENT_NAME,  
                         STUDENT_SEX,  
                         STUDENT_BIRTHDAY,  
                         CLASS_ID)  
  VALUES   (123456,  
            '某某某',  
            '女',  
            '1980-08-01',  
            121546  
           ); 

创建连接MySql使用的配置文件mysql.properties。

jdbc.driverClassName=com.mysql.jdbc.Driver  
jdbc.url=jdbc:mysql://localhost:3306/student_manager?user=root&password=limingnihao&useUnicode=true&characterEncoding=UTF-8&useSSL=false  

1.2.3 搭建MyBatis环境

顺序随便,现在的顺序是因为可以尽量的少的修改写好的文件。

1.2.3.1 创建实体类

Student实体类:StudentEntity。

public class StudentEntity implements Serializable {  

    private static final long serialVersionUID = 3096154202413606831L;  
    private ClassEntity classEntity;  
    private Date studentBirthday;  
    private String studentID;  
    private String studentName;  
    private String studentSex;  

    public ClassEntity getClassEntity() {  
        return classEntity;  
    }  

    public Date getStudentBirthday() {  
        return studentBirthday;  
    }  

    public String getStudentID() {  
        return studentID;  
    }  

    public String getStudentName() {  
        return studentName;  
    }  

    public String getStudentSex() {  
        return studentSex;  
    }  

    public void setClassEntity(ClassEntity classEntity) {  
        this.classEntity = classEntity;  
    }  

    public void setStudentBirthday(Date studentBirthday) {  
        this.studentBirthday = studentBirthday;  
    }  

    public void setStudentID(String studentID) {  
        this.studentID = studentID;  
    }  

    public void setStudentName(String studentName) {  
        this.studentName = studentName;  
    }  

    public void setStudentSex(String studentSex) {  
        this.studentSex = studentSex;  
    }  
}  
1.2.3.2 创建数据访问接口

Student类对应的dao接口:StudentMapper。

public interface StudentMapper {  

    public StudentEntity getStudent(String studentID);  

    public StudentEntity getStudentAndClass(String studentID);  

    public List<StudentEntity> getStudentAll();  

    public void insertStudent(StudentEntity entity);  

    public void deleteStudent(StudentEntity entity);  

    public void updateStudent(StudentEntity entity);  
}  
1.2.3.3 创建SQL映射语句文件

Student类的sql语句文件:StudentMapper.xml

resultMap标签:表字段与属性的映射。
Select标签:查询sql。

<?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="com.manager.data.StudentMapper">  
  
    <resultMap type="StudentEntity" id="studentResultMap">  
        <id property="studentID" column="STUDENT_ID"/>  
        <result property="studentName" column="STUDENT_NAME"/>  
        <result property="studentSex" column="STUDENT_SEX"/>  
        <result property="studentBirthday" column="STUDENT_BIRTHDAY"/>  
    </resultMap>  
      
    <!-- 查询学生,根据id -->  
    <select id="getStudent" parameterType="String" resultType="StudentEntity" resultMap="studentResultMap">  
        <![CDATA[ 
            SELECT * from STUDENT_TBL ST 
                WHERE ST.STUDENT_ID = #{studentID}  
        ]]>   
    </select>  
      
    <!-- 查询学生列表 -->  
    <select id="getStudentAll"  resultType="com.manager.data.model.StudentEntity" resultMap="studentResultMap">  
        <![CDATA[ 
            SELECT * from STUDENT_TBL 
        ]]>   
    </select>  
      
</mapper> 
1.2.3.4 创建MyBatis的mapper配置文件

在src/main/resource中创建MyBatis配置文件:mybatis-config.xml。

typeAliases标签:给类起一个别名。com.manager.data.model.StudentEntity类,可以使用StudentEntity代替。
Mappers标签:加载MyBatis中实体类的SQL映射语句文件。

<?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>  
    <typeAliases>  
        <typeAlias alias="StudentEntity" type="com.manager.data.model.StudentEntity"/>  
    </typeAliases>  
    <mappers>  
        <mapper resource="com/manager/data/maps/StudentMapper.xml" />  
    </mappers>  
</configuration>  
1.2.3.5 修改Spring的配置文件

主要是添加SqlSession的制作工厂类的bean:SqlSessionFactoryBean,(在mybatis.spring包中)。需要指定配置文件位置和dataSource。
和数据访问接口对应的实现bean。通过MapperFactoryBean创建出来。需要执行接口类全称和SqlSession工厂bean的引用。

<!-- 导入属性配置文件 -->  
<context:property-placeholder location="classpath:mysql.properties" />  
  
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">  
    <property name="driverClassName" value="${jdbc.driverClassName}" />  
    <property name="url" value="${jdbc.url}" />  
</bean>  
  
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
    <property name="dataSource" ref="dataSource" />  
</bean>  
  
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
    <property name="configLocation" value="classpath:mybatis-config.xml" />  
    <property name="dataSource" ref="dataSource" />  
</bean>  
  
<!— mapper bean -->  
<bean id="studentMapper" class="org.mybatis.spring.MapperFactoryBean">  
    <property name="mapperInterface" value="com.manager.data.StudentMapper" />  
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />  
</bean>  

也可以不定义mapper的bean,使用注解:

将StudentMapper加入注解

@Repository  
@Transactional  
public interface StudentMapper {  
}  

对应的需要在dispatcher-servlet.xml中加入扫描:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
    <property name="annotationClass" value="org.springframework.stereotype.Repository"/>  
    <property name="basePackage" value="com.liming.manager"/>  
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>  
</bean>  

1.2.4 测试StudentMapper

使用SpringMVC测试,创建一个TestController,配置tomcat,访问index.do页面进行测试:

@Controller  
public class TestController {  
  
    @Autowired  
    private StudentMapper studentMapper;  
      
    @RequestMapping(value = "index.do")  
    public void indexPage() {  
        StudentEntity entity = studentMapper.getStudent("10000013");  
        System.out.println("name:" + entity.getStudentName());  
    }  
}  

使用Junit测试:

使用Junit测试:  
Java代码  
@RunWith(value = SpringJUnit4ClassRunner.class)  
@ContextConfiguration(value = "test-servlet.xml")  
public class StudentMapperTest {  
      
    @Autowired  
    private ClassMapper classMapper;  
      
    @Autowired  
    private StudentMapper studentMapper;  
      
    @Transactional  
    public void getStudentTest(){  
        StudentEntity entity = studentMapper.getStudent("10000013");  
        System.out.println("" + entity.getStudentID() + entity.getStudentName());  
          
        List<StudentEntity> studentList = studentMapper.getStudentAll();  
        for( StudentEntity entityTemp : studentList){  
            System.out.println(entityTemp.getStudentName());  
        }  
    }  
    
}  

二 SQL语句映射文件(1) resultMap

SQL 映射XML 文件是所有sql语句放置的地方。需要定义一个workspace,一般定义为对应的接口类的路径。写好SQL语句映射文件后,需要在MyBAtis配置文件mappers标签中引用,例如:

<mappers>  
    <mapper resource="com/liming/manager/data/mappers/UserMapper.xml" />  
    <mapper resource="com/liming/manager/data/mappers/StudentMapper.xml" />  
    <mapper resource="com/liming/manager/data/mappers/ClassMapper.xml" />  
    <mapper resource="com/liming/manager/data/mappers/TeacherMapper.xml" />  
</mappers>  

当Java接口与XML文件在一个相对路径下时,可以不在myBatis配置文件的mappers中声明。

SQL 映射XML 文件一些初级的元素:

  1. cache – 配置给定模式的缓存

  2. cache-ref – 从别的模式中引用一个缓存

  3. resultMap – 这是最复杂而却强大的一个元素了,它描述如何从结果集中加载对象

  4. sql – 一个可以被其他语句复用的SQL 块

  5. insert – 映射INSERT 语句

  6. update – 映射UPDATE 语句

  7. delete – 映射DELEETE 语句

  8. select - 映射SELECT语句

2.1 resultMap

resultMap 是MyBatis 中最重要最强大的元素了。你可以让你比使用JDBC 调用结果集省掉90%的代码,也可以让你做许多JDBC 不支持的事。现实上,要写一个等同类似于交互的映射这样的复杂语句,可能要上千行的代码。ResultMaps 的目的,就是这样简单的语句而不需要多余的结果映射,更多复杂的语句,除了只要一些绝对必须的语句描述关系以外,再也不需要其它的。

resultMap属性:type为java实体类;id为此resultMap的标识。

resultMap可以设置的映射:

  1. constructor – 用来将结果反射给一个实例化好的类的构造器
    a) idArg – ID 参数;将结果集标记为ID,以方便全局调用
    b) arg –反射到构造器的通常结果

  2. id – ID 结果,将结果集标记为ID,以方便全局调用

  3. result – 反射到JavaBean 属性的普通结果

  4. association – 复杂类型的结合;多个结果合成的类型
    a) nested result mappings – 几resultMap 自身嵌套关联,也可以引用到一个其它上

  5. collection –复杂类型集合a collection of complex types

  6. nested result mappings – resultMap 的集合,也可以引用到一个其它上

  7. discriminator – 使用一个结果值以决定使用哪个resultMap
    a) case – 基本一些值的结果映射的case 情形

i. nested result mappings –一个case 情形本身就是一个结果映射,因此也可以包括一些相同的元素,也可以引用一个外部resultMap。

2.1.1 id、result

id、result是最简单的映射,id为主键映射;result其他基本数据库表字段到实体类属性的映射。
最简单的例子:

<resultMap type="liming.student.manager.data.model.StudentEntity" id="studentResultMap">  
    <id  property="studentId"        column="STUDENT_ID" javaType="String" jdbcType="VARCHAR"/>  
    <result property="studentName"       column="STUDENT_NAME" javaType="String" jdbcType="VARCHAR"/>  
    <result property="studentSex"        column="STUDENT_SEX"  javaType="int" jdbcType="INTEGER"/>  
    <result property="studentBirthday"   column="STUDENT_BIRTHDAY"  javaType="Date" jdbcType="DATE"/>  
    <result property="studentPhoto"  column="STUDENT_PHOTO" javaType="byte[]" jdbcType="BLOB" typeHandler="org.apache.ibatis.type.BlobTypeHandler" />  
</resultMap>  

id、result语句属性配置细节:

属性描述
property需要映射到JavaBean 的属性名称。
column数据表的列名或者标签别名。
javaType一个完整的类名,或者是一个类型别名。如果你匹配的是一个JavaBean,那MyBatis 通常会自行检测到。然后,如果你是要映射到一个HashMap,那你需要指定javaType 要达到的目的。
jdbcType数据表支持的类型列表。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。如果你是直接针对JDBC 编码,且有允许空的列,而你要指定这项。
typeHandler使用这个属性可以覆写类型处理器。这项值可以是一个完整的类名,也可以是一个类型别名。

支持的JDBC类型
为了将来的引用,MyBatis 支持下列JDBC 类型,通过JdbcType 枚举:
BIT,FLOAT,CHAR,TIMESTAMP,OTHER,UNDEFINED,TINYINT,REAL,VARCHAR,BINARY,BLOB,NVARCHAR,SMALLINT,DOUBLE,LONGVARCHAR,VARBINARY,CLOB,NCHAR,INTEGER,NUMERIC,DATE,LONGVARBINARY,BOOLEAN,NCLOB,BIGINT,DECIMAL,TIME,NULL,CURSOR

2.1.2 constructor

我们使用id、result时候,需要定义java实体类的属性映射到数据库表的字段上。这个时候是使用JavaBean实现的。当然我们也可以使用实体类的构造方法来实现值的映射,这个时候是通过构造方法参数的书写的顺序来进行赋值的。
使用construcotr功能有限(例如使用collection级联查询)。
上面使用id、result实现的功能就可以改为:

<resultMap type="StudentEntity" id="studentResultMap" >  
    <constructor>  
        <idArg javaType="String" column="STUDENT_ID"/>  
        <arg javaType="String" column="STUDENT_NAME"/>  
        <arg javaType="String" column="STUDENT_SEX"/>  
        <arg javaType="Date" column="STUDENT_BIRTHDAY"/>  
    </constructor>  
</resultMap> 

当然,我们需要定义StudentEntity实体类的构造方法:

public StudentEntity(String studentID, String studentName, String studentSex, Date studentBirthday){  
    this.studentID = studentID;  
    this.studentName = studentName;  
    this.studentSex = studentSex;  
    this.studentBirthday = studentBirthday;  
}

2.1.3 association联合

联合元素用来处理“一对一”的关系。需要指定映射的Java实体类的属性,属性的javaType(通常MyBatis 自己会识别)。对应的数据库表的列名称。如果想覆写的话返回结果的值,需要指定typeHandler。
不同情况需要告诉MyBatis 如何加载一个联合。MyBatis 可以用两种方式加载:

  1. select: 执行一个其它映射的SQL 语句返回一个Java实体类型。较灵活;

  2. resultsMap: 使用一个嵌套的结果映射来处理通过join查询结果集,映射成Java实体类型。

例如,一个班级对应一个班主任。
首先定义好班级中的班主任属性:

private TeacherEntity teacherEntity; 
2.1.3.1 使用select实现联合

例:班级实体类中有班主任的属性,通过联合在得到一个班级实体时,同时映射出班主任实体。

这样可以直接复用在TeacherMapper.xml文件中定义好的查询teacher根据其ID的select语句。而且不需要修改写好的SQL语句,只需要直接修改resultMap即可。

ClassMapper.xml文件部分内容:

<resultMap type="ClassEntity" id="classResultMap">  
    <id property="classID" column="CLASS_ID" />  
    <result property="className" column="CLASS_NAME" />  
    <result property="classYear" column="CLASS_YEAR" />  
    <association property="teacherEntity" column="TEACHER_ID" select="getTeacher"/>  
</resultMap>  
  
<select id="getClassByID" parameterType="String" resultMap="classResultMap">  
    SELECT * FROM CLASS_TBL CT  
    WHERE CT.CLASS_ID = #{classID};  
</select>  

TeacherMapper.xml文件部分内容:

<resultMap type="TeacherEntity" id="teacherResultMap">  
    <id property="teacherID" column="TEACHER_ID" />  
    <result property="teacherName" column="TEACHER_NAME" />  
    <result property="teacherSex" column="TEACHER_SEX" />  
    <result property="teacherBirthday" column="TEACHER_BIRTHDAY"/>  
    <result property="workDate" column="WORK_DATE"/>  
    <result property="professional" column="PROFESSIONAL"/>  
</resultMap>  
  
<select id="getTeacher" parameterType="String"  resultMap="teacherResultMap">  
    SELECT *  
      FROM TEACHER_TBL TT  
     WHERE TT.TEACHER_ID = #{teacherID}  
</select>  
2.1.3.2 使用resultMap实现联合

与上面同样的功能,查询班级,同时查询器班主任。需在association中添加resultMap(在teacher的xml文件中定义好的),新写sql(查询班级表left join教师表),不需要teacher的select。
修改ClassMapper.xml文件部分内容:

<resultMap type="ClassEntity" id="classResultMap">  
    <id property="classID" column="CLASS_ID" />  
    <result property="className" column="CLASS_NAME" />  
    <result property="classYear" column="CLASS_YEAR" />  
    <association property="teacherEntity" column="TEACHER_ID"  resultMap="teacherResultMap"/>  
</resultMap>  
  
<select id="getClassAndTeacher" parameterType="String" resultMap="classResultMap">  
    SELECT *  
      FROM CLASS_TBL CT LEFT JOIN TEACHER_TBL TT ON CT.TEACHER_ID = TT.TEACHER_ID  
     WHERE CT.CLASS_ID = #{classID};  
</select>  

其中的teacherResultMap请见上面TeacherMapper.xml文件部分内容中。

2.1.4 collection聚集

聚集元素用来处理“一对多”的关系。需要指定映射的Java实体类的属性,属性的javaType(一般为ArrayList);列表中对象的类型ofType(Java实体类);对应的数据库表的列名称;
不同情况需要告诉MyBatis 如何加载一个聚集。MyBatis 可以用两种方式加载:

  1. select: 执行一个其它映射的SQL 语句返回一个Java实体类型。较灵活;

  2. resultsMap: 使用一个嵌套的结果映射来处理通过join查询结果集,映射成Java实体类型。

例如,一个班级有多个学生。
首先定义班级中的学生列表属性:

private List<StudentEntity> studentList;  
2.1.4.1 使用select实现聚集

用法和联合很类似,区别在于,这是一对多,所以一般映射过来的都是列表。所以这里需要 定义javaType为ArrayList,还需要定义列表中对象的类型ofType,以及必须设置的select的语句名称(需要注意的是,这里的查询 student的select语句条件必须是外键classID)。

ClassMapper.xml文件部分内容:

<resultMap type="ClassEntity" id="classResultMap">  
    <id property="classID" column="CLASS_ID" />  
    <result property="className" column="CLASS_NAME" />  
    <result property="classYear" column="CLASS_YEAR" />  
    <association property="teacherEntity" column="TEACHER_ID"  select="getTeacher"/>  
    <collection property="studentList" column="CLASS_ID" javaType="ArrayList" ofType="StudentEntity" select="getStudentByClassID"/>  
</resultMap>  
  
<select id="getClassByID" parameterType="String" resultMap="classResultMap">  
    SELECT * FROM CLASS_TBL CT  
    WHERE CT.CLASS_ID = #{classID};  
</select>  

StudentMapper.xml文件部分内容:

<!-- java属性,数据库表字段之间的映射定义 -->  
<resultMap type="StudentEntity" id="studentResultMap">  
    <id property="studentID" column="STUDENT_ID" />  
    <result property="studentName" column="STUDENT_NAME" />  
    <result property="studentSex" column="STUDENT_SEX" />  
    <result property="studentBirthday" column="STUDENT_BIRTHDAY" />  
</resultMap>  
  
<!-- 查询学生list,根据班级id -->  
<select id="getStudentByClassID" parameterType="String" resultMap="studentResultMap">  
    <include refid="selectStudentAll" />  
    WHERE ST.CLASS_ID = #{classID}  
</select>  
2.1.4.2 使用resultMap实现聚集

使用resultMap,就需要重写一个sql,left join学生表。

<resultMap type="ClassEntity" id="classResultMap">  
    <id property="classID" column="CLASS_ID" />  
    <result property="className" column="CLASS_NAME" />  
    <result property="classYear" column="CLASS_YEAR" />  
    <association property="teacherEntity" column="TEACHER_ID"  resultMap="teacherResultMap"/>  
    <collection property="studentList" column="CLASS_ID" javaType="ArrayList" ofType="StudentEntity" resultMap="studentResultMap"/>  
</resultMap>  
  
<select id="getClassAndTeacherStudent" parameterType="String" resultMap="classResultMap">  
    SELECT *  
      FROM CLASS_TBL CT  
           LEFT JOIN STUDENT_TBL ST  
              ON CT.CLASS_ID = ST.CLASS_ID  
           LEFT JOIN TEACHER_TBL TT  
              ON CT.TEACHER_ID = TT.TEACHER_ID  
      WHERE CT.CLASS_ID = #{classID};  
</select>  

其中的teacherResultMap请见上面TeacherMapper.xml文件部分内容中。studentResultMap请见上面StudentMapper.xml文件部分内容中。

2.1.5 discriminator鉴别器

有时一个单独的数据库查询也许返回很多不同(但是希望有些关联)数据类型的结果集。鉴别器元素就是被设计来处理这个情况的,还有包括类的继承层次结构。鉴别器非常容易理解,因为它的表现很像Java语言中的switch语句。

定义鉴别器指定了column和javaType属性。列是MyBatis查找比较值的地方。JavaType是需要被用来保证等价测试的合适类型(尽管字符串在很多情形下都会有用)。

下面这个例子为,当classId为20000001时,才映射classId属性。

<resultMap type="liming.student.manager.data.model.StudentEntity" id="resultMap_studentEntity_discriminator">  
    <id  property="studentId"        column="STUDENT_ID" javaType="String" jdbcType="VARCHAR"/>  
    <result property="studentName"       column="STUDENT_NAME" javaType="String" jdbcType="VARCHAR"/>  
    <result property="studentSex"        column="STUDENT_SEX"  javaType="int" jdbcType="INTEGER"/>  
    <result property="studentBirthday"   column="STUDENT_BIRTHDAY"  javaType="Date" jdbcType="DATE"/>  
    <result property="studentPhoto"  column="STUDENT_PHOTO" javaType="byte[]" jdbcType="BLOB" typeHandler="org.apache.ibatis.type.BlobTypeHandler" />  
    <result property="placeId"           column="PLACE_ID" javaType="String" jdbcType="VARCHAR"/>  
    <discriminator column="CLASS_ID" javaType="String" jdbcType="VARCHAR">  
        <case value="20000001" resultType="liming.student.manager.data.model.StudentEntity" >  
            <result property="classId" column="CLASS_ID" javaType="String" jdbcType="VARCHAR"/>  
        </case>  
    </discriminator>  
</resultMap>  

三 SQL语句映射文件(2) 增删改查、参数、缓存

2.2 select

一个select 元素非常简单。例如:

<!-- 查询学生,根据id -->  
<select id="getStudent" parameterType="String" resultMap="studentResultMap">  
    SELECT ST.STUDENT_ID,  
               ST.STUDENT_NAME,  
               ST.STUDENT_SEX,  
               ST.STUDENT_BIRTHDAY,  
               ST.CLASS_ID  
          FROM STUDENT_TBL ST  
         WHERE ST.STUDENT_ID = #{studentID}  
</select>  

这条语句就叫做‘getStudent,有一个String参数,并返回一个StudentEntity类型的对象。
注意参数的标识是:#{studentID}。

select 语句属性配置细节:

属性描述取值默认
id在这个模式下唯一的标识符,可被其它语句引用
parameterType传给此语句的参数的完整类名或别名
resultType语句返回值类型的整类名或别名。注意,如果是集合,那么这里填写的是集合的项的整类名或别名,而不是集合本身的类名。(resultType 与resultMap 不能并用)
resultMap引用的外部resultMap 名。结果集映射是MyBatis 中最强大的特性。许多复杂的映射都可以轻松解决。(resultType 与resultMap 不能并用)
flushCache如果设为true,则会在每次语句调用的时候就会清空缓存。select 语句默认设为falsetrue|falsefalse
useCache如果设为true,则语句的结果集将被缓存。select 语句默认设为false true|false false timeout 设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定true|falsefalse
timeout设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定正整数未设置
fetchSize设置一个值后,驱动器会在结果集数目达到此数值后,激发返回,默认为不设值,由驱动器自己决定正整数驱动器决定
statementTypestatement,preparedstatement,callablestatement。 预准备语句、可调用语句STATEMENT PREPARED CALLABLEPREPARED
resultSetTypeforward_only,scroll_sensitive,scroll_insensitive 只转发,滚动敏感,不区分大小写的滚动FORWARD_ONLY SCROLL_SENSITIVE SCROLL_INSENSITIVE驱动器决定

2.3 insert

一个简单的insert语句:

<!-- 插入学生 -->  
<insert id="insertStudent" parameterType="StudentEntity">  
        INSERT INTO STUDENT_TBL (STUDENT_ID,  
                                          STUDENT_NAME,  
                                          STUDENT_SEX,  
                                          STUDENT_BIRTHDAY,  
                                          CLASS_ID)  
              VALUES   (#{studentID},  
                          #{studentName},  
                          #{studentSex},  
                          #{studentBirthday},  
                          #{classEntity.classID})  
</insert>  

insert可以使用数据库支持的自动生成主键策略,设置useGeneratedKeys=”true”,然后把keyProperty 设成对应的列,就搞定了。比如说上面的StudentEntity 使用auto-generated 为id 列生成主键。
还可以使用selectKey元素。下面例子,使用mysql数据库nextval(‘student’)为自定义函数,用来生成一个key。

<!-- 插入学生 自动主键-->  
<insert id="insertStudentAutoKey" parameterType="StudentEntity">  
    <selectKey keyProperty="studentID" resultType="String" order="BEFORE">  
            select nextval('student')  
    </selectKey>  
        INSERT INTO STUDENT_TBL (STUDENT_ID,  
                                 STUDENT_NAME,  
                                 STUDENT_SEX,  
                                 STUDENT_BIRTHDAY,  
                                 CLASS_ID)  
              VALUES   (#{studentID},  
                        #{studentName},  
                        #{studentSex},  
                        #{studentBirthday},  
                        #{classEntity.classID})      
</insert>  

insert语句属性配置细节:

属性描述取值默认
id在这个模式下唯一的标识符,可被其它语句引用
parameterType传给此语句的参数的完整类名或别名
flushCache如果设为true,则会在每次语句调用的时候就会清空缓存。select 语句默认设为falsetrue|falsefalse
useCache如果设为true,则语句的结果集将被缓存。select 语句默认设为false true|false false timeout 设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定true|falsefalse
timeout设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定正整数未设置
fetchSize设置一个值后,驱动器会在结果集数目达到此数值后,激发返回,默认为不设值,由驱动器自己决定正整数驱动器决定
statementTypestatement,preparedstatement,callablestatement。 预准备语句、可调用语句STATEMENT PREPARED CALLABLEPREPARED
useGeneratedKeys告诉MyBatis 使用JDBC 的getGeneratedKeys 方法来获取数据库自己生成的主键(MySQL、SQLSERVER 等 关系型数据库会有自动生成的字段)。默认:falsetrue|falsefalse
keyProperty标识一个将要被MyBatis 设置进getGeneratedKeys 的key 所返回的值,或者为insert 语句使用一个selectKey 子元素。

selectKey语句属性配置细节:

属性描述取值
keyPropertyselectKey 语句生成结果需要设置的属性。
resultType生成结果类型,MyBatis 允许使用基本的数据类型,包括String 、int类型。
order可以设成BEFORE 或者AFTER,如果设为BEFORE,那它会先选择主键,然后设置keyProperty,再执行insert语句;如果设为AFTER,它就先运行 insert 语句再运行selectKey 语句,通常是insert 语句中内部调用数据库(像Oracle)内嵌的序列机制。BEFORE AFTER
statementType像上面的那样, MyBatis 支持STATEMENT,PREPARED和CALLABLE 的语句形式, 对应Statement ,PreparedStatement 和CallableStatement 响应STATEMENT PREPARED CALLABLE

2.4 update、delete

一个简单的update:

<!-- 更新学生信息 -->  
<update id="updateStudent" parameterType="StudentEntity">  
        UPDATE STUDENT_TBL  
            SET STUDENT_TBL.STUDENT_NAME = #{studentName},   
                STUDENT_TBL.STUDENT_SEX = #{studentSex},  
                STUDENT_TBL.STUDENT_BIRTHDAY = #{studentBirthday},  
                STUDENT_TBL.CLASS_ID = #{classEntity.classID}  
         WHERE STUDENT_TBL.STUDENT_ID = #{studentID};     
</update>  

一个简单的delete:

<!-- 删除学生 -->  
<delete id="deleteStudent" parameterType="StudentEntity">  
        DELETE FROM STUDENT_TBL WHERE STUDENT_ID = #{studentID}  
</delete>  

update、delete语句属性配置细节:

属性描述取值默认
id在这个模式下唯一的标识符,可被其它语句引用
parameterType传给此语句的参数的完整类名或别名
flushCache如果设为true,则会在每次语句调用的时候就会清空缓存。select 语句默认设为falsetrue|falsefalse
useCache如果设为true,则语句的结果集将被缓存。select 语句默认设为false true|false false timeout 设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定true|falsefalse
timeout设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定正整数未设置
fetchSize设置一个值后,驱动器会在结果集数目达到此数值后,激发返回,默认为不设值,由驱动器自己决定正整数驱动器决定
statementTypestatement,preparedstatement,callablestatement。 预准备语句、可调用语句STATEMENT PREPARED CALLABLEPREPARED

2.5 sql

Sql元素用来定义一个可以复用的SQL 语句段,供其它语句调用。比如:

<!-- 复用sql语句  查询student表所有字段 -->  
<sql id="selectStudentAll">  
        SELECT ST.STUDENT_ID,  
                   ST.STUDENT_NAME,  
                   ST.STUDENT_SEX,  
                   ST.STUDENT_BIRTHDAY,  
                   ST.CLASS_ID  
              FROM STUDENT_TBL ST  
</sql>  

这样,在select的语句中就可以直接引用使用了,将上面select语句改成:

<!-- 查询学生,根据id -->  
<select id="getStudent" parameterType="String" resultMap="studentResultMap">  
    <include refid="selectStudentAll"/>  
            WHERE ST.STUDENT_ID = #{studentID}   
</select>  

2.6 parameters

上面很多地方已经用到了参数,比如查询、修改、删除的条件,插入,修改的数据等,MyBatis可以使用的基本数据类型和Java的复杂数据类型。
基本数据类型,String,int,date等。
但是使用基本数据类型,只能提供一个参数,所以需要使用Java实体类,或Map类型做参数类型。通过#{}可以直接得到其属性。

2.6.1 基本类型参数

根据入学时间,检索学生列表:

<!-- 查询学生list,根据入学时间  -->  
<select id="getStudentListByDate"  parameterType="Date" resultMap="studentResultMap"> 
    SELECT *  
      FROM STUDENT_TBL ST LEFT JOIN CLASS_TBL CT ON ST.CLASS_ID = CT.CLASS_ID  
     WHERE CT.CLASS_YEAR = #{classYear};      
</select> 
List<StudentEntity> studentList = studentMapper.getStudentListByClassYear(StringUtil.parse("2007-9-1"));  
for (StudentEntity entityTemp : studentList) {  
    System.out.println(entityTemp.toString());  
}  

2.6.2 Java实体类型参数

根据姓名和性别,检索学生列表。使用实体类做参数:

<!-- 查询学生list,like姓名、=性别,参数entity类型 -->  
<select id="getStudentListWhereEntity" parameterType="StudentEntity" resultMap="studentResultMap">  
    SELECT * from STUDENT_TBL ST  
    WHERE ST.STUDENT_NAME LIKE CONCAT(CONCAT('%', #{studentName}),'%')  
    AND ST.STUDENT_SEX = #{studentSex}  
</select>  
StudentEntity entity = new StudentEntity();  
entity.setStudentName("李");  
entity.setStudentSex("男");  
List<StudentEntity> studentList = studentMapper.getStudentListWhereEntity(entity);  
for (StudentEntity entityTemp : studentList) {  
    System.out.println(entityTemp.toString());  
}  

2.6.3 Map参数

根据姓名和性别,检索学生列表。使用Map做参数:

<!-- 查询学生list,=性别,参数map类型 -->  
<select id="getStudentListWhereMap" parameterType="Map" resultMap="studentResultMap"> 
    SELECT * from STUDENT_TBL ST  
     WHERE ST.STUDENT_SEX = #{sex}  
          AND ST.STUDENT_SEX = #{sex}  
</select>    
Map<String, String> map = new HashMap<String, String>();  
map.put("sex", "女");  
map.put("name", "李");  
List<StudentEntity> studentList = studentMapper.getStudentListWhereMap(map);  
for (StudentEntity entityTemp : studentList) {  
    System.out.println(entityTemp.toString());  
}  

2.6.4 多参数的实现

如果想传入多个参数,则需要在接口的参数上添加@Param注解。给出一个实例:
接口写法:

public List<StudentEntity> getStudentListWhereParam(@Param(value = "name") String name, @Param(value = "sex") String sex, @Param(value = "birthday") Date birthdar, @Param(value = "classEntity") ClassEntity classEntity);  

SQL写法:

<!-- 查询学生list,like姓名、=性别、=生日、=班级,多参数方式 -->  
<select id="getStudentListWhereParam" resultMap="studentResultMap">  
    SELECT * from STUDENT_TBL ST  
    <where>  
        <if test="name!=null and name!='' ">  
            ST.STUDENT_NAME LIKE CONCAT(CONCAT('%', #{name}),'%')  
        </if>  
        <if test="sex!= null and sex!= '' ">  
            AND ST.STUDENT_SEX = #{sex}  
        </if>  
        <if test="birthday!=null">  
            AND ST.STUDENT_BIRTHDAY = #{birthday}  
        </if>  
        <if test="classEntity!=null and classEntity.classID !=null and classEntity.classID!='' ">  
            AND ST.CLASS_ID = #{classEntity.classID}  
        </if>  
    </where>  
</select>  

进行查询:

List<StudentEntity> studentList = studentMapper.getStudentListWhereParam("", "",StringUtil.parse("1985-05-28"), classMapper.getClassByID("20000002"));  
for (StudentEntity entityTemp : studentList) {  
    System.out.println(entityTemp.toString());  
}  

2.6.5 字符串代入法

默认的情况下,使用#{}语法会促使MyBatis 生成PreparedStatement 属性并且使用PreparedStatement 的参数(=?)来安全的设置值。尽量这些是快捷安全,也是经常使用的。但有时候你可能想直接未更改的字符串代入到SQL 语句中。比如说,对于ORDER BY,你可能会这样使用:ORDER BY ${columnName}但MyBatis 不会修改和规避掉这个字符串。
注意:这样地接收和应用一个用户输入到未更改的语句中,是非常不安全的。这会让用户能植入破坏代码,所以,要么要求字段不要允许客户输入,要么你直接来检测他的合法性 。

2.7 cache缓存

MyBatis 包含一个强在的、可配置、可定制的缓存机制。MyBatis 3 的缓存实现有了许多改进,既强劲也更容易配置。默认的情况,缓存是没有开启,除了会话缓存以外,它可以提高性能,且能解决全局依赖。开启二级缓存,你只需 要在SQL 映射文件中加入简单的一行:<cache/>

这句简单的语句的作用如下:

  1. 所有在映射文件里的select 语句都将被缓存。

  2. 所有在映射文件里insert,update 和delete 语句会清空缓存。

  3. 缓存使用“最近很少使用”算法来回收

  4. 缓存不会被设定的时间所清空。

  5. 每个缓存可以存储1024 个列表或对象的引用(不管查询出来的结果是什么)。

  6. 缓存将作为“读/写”缓存,意味着获取的对象不是共享的且对调用者是安全的。不会有其它的调用。

  7. 者或线程潜在修改。

例如,创建一个FIFO 缓存让60 秒就清空一次,存储512 个对象结果或列表引用,并且返回的结果是只读。因为在不用的线程里的两个调用者修改它们可能会导致引用冲突。

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true">  
</cache> 

还可以在不同的命名空间里共享同一个缓存配置或者实例。在这种情况下,你就可以使用cache-ref 来引用另外一个缓存。

<cache-ref namespace="com.liming.manager.data.StudentMapper"/> 

Cache 语句属性配置细节:

属性说明取值默认值
eviction缓存策略: LRU - 最近最少使用法:移出最近较长周期内都没有被使用的对象。 FIFI- 先进先出:移出队列里较早的对象 SOFT - 软引用:基于软引用规则,使用垃圾回收机制来移出对象 WEAK - 弱引用:基于弱引用规则,使用垃圾回收机制来强制性地移出对象LRU FIFI SOFT WEAKLRU
flushInterval代表一个合理的毫秒总计时间。默认是不设置,因此使用无间隔清空即只能调用语句来清空。正整数不设置
size缓存的对象的大小正整数1024
readOnly只读缓存将对所有调用者返回同一个实例。因此都不能被修改,这可以极大的提高性能。可写的缓存将通过序列 化来返回一个缓存对象的拷贝。这会比较慢,但是比较安全。所以默认值是false。true|falsefalse

四 动态SQL语句

有些时候,sql语句where条件中,需要一些安全判断,例如按某一条件查询时如果传入的参数是空,此时查询出的结果很可能是空的,也许我们需要参数为空时,是查出全部的信息。使用Oracle的序列、mysql的函数生成Id。这时我们可以使用动态sql。

下文均采用mysql语法和函数(例如字符串链接函数CONCAT)。

3.1 selectKey标签

在insert语句中,在Oracle经常使用序列、在MySQL中使用函数来自动生成插入表的主键,而且需要方法能返回这个生成主键。使用myBatis的selectKey标签可以实现这个效果。

下面例子,使用mysql数据库自定义函数nextval(‘student’),用来生成一个key,并把他设置到传入的实体类中的studentId属性上。所以在执行完此方法后,边可以通过这个实体类获取生成的key。

<!-- 1 selectKey(自动生表主键) - 插入学生 自动主键-->  
<insert id="createStudentAutoKey" parameterType="liming.student.manager.data.model.StudentEntity" keyProperty="studentId">  
    <selectKey keyProperty="studentId" resultType="String" order="BEFORE">  
        select nextval('student')  
    </selectKey>  
    INSERT INTO STUDENT_TBL(STUDENT_ID,  
                            STUDENT_NAME,  
                            STUDENT_SEX,  
                            STUDENT_BIRTHDAY,  
                            STUDENT_PHOTO,  
                            CLASS_ID,  
                            PLACE_ID)  
    VALUES (#{studentId},  
            #{studentName},  
            #{studentSex},  
            #{studentBirthday},  
            #{studentPhoto, javaType=byte[], jdbcType=BLOB, typeHandler=org.apache.ibatis.type.BlobTypeHandler},  
            #{classId},  
            #{placeId})  
</insert>  

调用接口方法,和获取自动生成key

StudentEntity entity = new StudentEntity();  
entity.setStudentName("黎明你好");  
entity.setStudentSex(1);  
entity.setStudentBirthday(DateUtil.parse("1985-05-28"));  
entity.setClassId("20000001");  
entity.setPlaceId("70000001");  
this.dynamicSqlMapper.createStudentAutoKey(entity);  
System.out.println("新增学生ID: " + entity.getStudentId());  

selectKey语句属性配置细节:

属性描述取值
keyPropertyselectKey 语句生成结果需要设置的属性。
resultType生成结果类型,MyBatis 允许使用基本的数据类型,包括String 、int类型。
order1:BEFORE,会先选择主键,然后设置keyProperty,再执行insert语句; 2:AFTER,就先运行insert 语句再运行selectKey 语句。BEFORE AFTER
statementTypeMyBatis 支持STATEMENT,PREPARED和CALLABLE 的语句形式, 对应Statement ,PreparedStatement 和CallableStatement 响应STATEMENT PREPARED CALLABLE

3.2 if标签

if标签可用在许多类型的sql语句中,我们以查询为例。首先看一个很普通的查询:

<!-- 查询学生list,like姓名 -->  
<select id="getStudentListLikeName" parameterType="StudentEntity" resultMap="studentResultMap">  
    SELECT * from STUDENT_TBL ST   
WHERE ST.STUDENT_NAME LIKE CONCAT(CONCAT('%', #{studentName}),'%')  
</select>  

但是此时如果studentName或studentSex为null,此语句很可能报错或查询结果为空。此时我们使用if动态sql语句先进行判断,如果值为null或等于空字符串,我们就不进行此条件的判断,增加灵活性。

参数为实体类StudentEntity。将实体类中所有的属性均进行判断,如果不为空则执行判断条件。

<!-- 2 if(判断参数) - 将实体类不为空的属性作为where条件 -->  
<select id="getStudentList_if" resultMap="resultMap_studentEntity" parameterType="liming.student.manager.data.model.StudentEntity">  
    SELECT ST.STUDENT_ID,  
           ST.STUDENT_NAME,  
           ST.STUDENT_SEX,  
           ST.STUDENT_BIRTHDAY,  
           ST.STUDENT_PHOTO,  
           ST.CLASS_ID,  
           ST.PLACE_ID  
      FROM STUDENT_TBL ST   
     WHERE  
    <if test="studentName !=null ">  
        ST.STUDENT_NAME LIKE CONCAT(CONCAT('%', #{studentName, jdbcType=VARCHAR}),'%')  
    </if>  
    <if test="studentSex != null and studentSex != '' ">  
        AND ST.STUDENT_SEX = #{studentSex, jdbcType=INTEGER}  
    </if>  
    <if test="studentBirthday != null ">  
        AND ST.STUDENT_BIRTHDAY = #{studentBirthday, jdbcType=DATE}  
    </if>  
    <if test="classId != null and classId!= '' ">  
        AND ST.CLASS_ID = #{classId, jdbcType=VARCHAR}  
    </if>  
    <if test="classEntity != null and classEntity.classId !=null and classEntity.classId !=' ' ">  
        AND ST.CLASS_ID = #{classEntity.classId, jdbcType=VARCHAR}  
    </if>  
    <if test="placeId != null and placeId != '' ">  
        AND ST.PLACE_ID = #{placeId, jdbcType=VARCHAR}  
    </if>  
    <if test="placeEntity != null and placeEntity.placeId != null and placeEntity.placeId != '' ">  
        AND ST.PLACE_ID = #{placeEntity.placeId, jdbcType=VARCHAR}  
    </if>  
    <if test="studentId != null and studentId != '' ">  
        AND ST.STUDENT_ID = #{studentId, jdbcType=VARCHAR}  
    </if>   
</select>  

使用时比较灵活, new一个这样的实体类,我们需要限制那个条件,只需要附上相应的值就会where这个条件,相反不去赋值就可以不在where中判断。

public void select_test_2_1() {  
    StudentEntity entity = new StudentEntity();  
    entity.setStudentName("");  
    entity.setStudentSex(1);  
    entity.setStudentBirthday(DateUtil.parse("1985-05-28"));  
    entity.setClassId("20000001");  
    //entity.setPlaceId("70000001");  
    List<StudentEntity> list = this.dynamicSqlMapper.getStudentList_if(entity);  
    for (StudentEntity e : list) {  
        System.out.println(e.toString());  
    }  
}  

3.3 if + where的条件判断

当where中的条件使用的if标签较多时,这样的组合可能会导致错误。我们以在3.1中的查询语句为例子,当java代码按如下方法调用时:

@Test  
public void select_test_2_1() {  
    StudentEntity entity = new StudentEntity();  
    entity.setStudentName(null);  
    entity.setStudentSex(1);  
    List<StudentEntity> list = this.dynamicSqlMapper.getStudentList_if(entity);  
    for (StudentEntity e : list) {  
        System.out.println(e.toString());  
    }  
}  

如果上面例子,参数studentName为null,将不会进行STUDENT_NAME列的判断,则会直接导“WHERE AND”关键字多余的错误SQL。

这时我们可以使用where动态语句来解决。这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。

上面例子修改为:

<!-- 3 select - where/if(判断参数) - 将实体类不为空的属性作为where条件 -->  
<select id="getStudentList_whereIf" resultMap="resultMap_studentEntity" parameterType="liming.student.manager.data.model.StudentEntity">  
    SELECT ST.STUDENT_ID,  
           ST.STUDENT_NAME,  
           ST.STUDENT_SEX,  
           ST.STUDENT_BIRTHDAY,  
           ST.STUDENT_PHOTO,  
           ST.CLASS_ID,  
           ST.PLACE_ID  
      FROM STUDENT_TBL ST   
    <where>  
        <if test="studentName !=null ">  
            ST.STUDENT_NAME LIKE CONCAT(CONCAT('%', #{studentName, jdbcType=VARCHAR}),'%')  
        </if>  
        <if test="studentSex != null and studentSex != '' ">  
            AND ST.STUDENT_SEX = #{studentSex, jdbcType=INTEGER}  
        </if>  
        <if test="studentBirthday != null ">  
            AND ST.STUDENT_BIRTHDAY = #{studentBirthday, jdbcType=DATE}  
        </if>  
        <if test="classId != null and classId!= '' ">  
            AND ST.CLASS_ID = #{classId, jdbcType=VARCHAR}  
        </if>  
        <if test="classEntity != null and classEntity.classId !=null and classEntity.classId !=' ' ">  
            AND ST.CLASS_ID = #{classEntity.classId, jdbcType=VARCHAR}  
        </if>  
        <if test="placeId != null and placeId != '' ">  
            AND ST.PLACE_ID = #{placeId, jdbcType=VARCHAR}  
        </if>  
        <if test="placeEntity != null and placeEntity.placeId != null and placeEntity.placeId != '' ">  
            AND ST.PLACE_ID = #{placeEntity.placeId, jdbcType=VARCHAR}  
        </if>  
        <if test="studentId != null and studentId != '' ">  
            AND ST.STUDENT_ID = #{studentId, jdbcType=VARCHAR}  
        </if>  
    </where>    
</select>  

3.4 if + set的更新语句

当update语句中没有使用if标签时,如果有一个参数为null,都会导致错误。

当在update语句中使用if标签时,如果前面的if没有执行,则或导致逗号多余错误。使用set标签可以将动态的配置SET 关键字,和剔除追加到条件末尾的任何不相关的逗号。

使用if+set标签修改后,如果某项为null则不进行更新,而是保持数据库原值。如下示例:

<!-- 4 if/set(判断参数) - 将实体类不为空的属性更新 -->  
<update id="updateStudent_if_set" parameterType="liming.student.manager.data.model.StudentEntity">  
    UPDATE STUDENT_TBL  
    <set>  
        <if test="studentName != null and studentName != '' ">  
            STUDENT_TBL.STUDENT_NAME = #{studentName},  
        </if>  
        <if test="studentSex != null and studentSex != '' ">  
            STUDENT_TBL.STUDENT_SEX = #{studentSex},  
        </if>  
        <if test="studentBirthday != null ">  
            STUDENT_TBL.STUDENT_BIRTHDAY = #{studentBirthday},  
        </if>  
        <if test="studentPhoto != null ">  
            STUDENT_TBL.STUDENT_PHOTO = #{studentPhoto, javaType=byte[], jdbcType=BLOB, typeHandler=org.apache.ibatis.type.BlobTypeHandler},  
        </if>  
        <if test="classId != '' ">  
            STUDENT_TBL.CLASS_ID = #{classId}  
        </if>  
        <if test="placeId != '' ">  
            STUDENT_TBL.PLACE_ID = #{placeId}  
        </if>  
    </set>  
    WHERE STUDENT_TBL.STUDENT_ID = #{studentId};      
</update>  

3.5 if + trim代替where/set标签

trim是更灵活的去处多余关键字的标签,他可以实践where和set的效果。

3.5.1 trim代替where

<!-- 5.1 if/trim代替where(判断参数) - 将实体类不为空的属性作为where条件 -->  
<select id="getStudentList_if_trim" resultMap="resultMap_studentEntity">  
    SELECT ST.STUDENT_ID,  
           ST.STUDENT_NAME,  
           ST.STUDENT_SEX,  
           ST.STUDENT_BIRTHDAY,  
           ST.STUDENT_PHOTO,  
           ST.CLASS_ID,  
           ST.PLACE_ID  
      FROM STUDENT_TBL ST   
    <trim prefix="WHERE" prefixOverrides="AND|OR">  
        <if test="studentName !=null ">  
            ST.STUDENT_NAME LIKE CONCAT(CONCAT('%', #{studentName, jdbcType=VARCHAR}),'%')  
        </if>  
        <if test="studentSex != null and studentSex != '' ">  
            AND ST.STUDENT_SEX = #{studentSex, jdbcType=INTEGER}  
        </if>  
        <if test="studentBirthday != null ">  
            AND ST.STUDENT_BIRTHDAY = #{studentBirthday, jdbcType=DATE}  
        </if>  
        <if test="classId != null and classId!= '' ">  
            AND ST.CLASS_ID = #{classId, jdbcType=VARCHAR}  
        </if>  
        <if test="classEntity != null and classEntity.classId !=null and classEntity.classId !=' ' ">  
            AND ST.CLASS_ID = #{classEntity.classId, jdbcType=VARCHAR}  
        </if>  
        <if test="placeId != null and placeId != '' ">  
            AND ST.PLACE_ID = #{placeId, jdbcType=VARCHAR}  
        </if>  
        <if test="placeEntity != null and placeEntity.placeId != null and placeEntity.placeId != '' ">  
            AND ST.PLACE_ID = #{placeEntity.placeId, jdbcType=VARCHAR}  
        </if>  
        <if test="studentId != null and studentId != '' ">  
            AND ST.STUDENT_ID = #{studentId, jdbcType=VARCHAR}  
        </if>  
    </trim>     
</select>  

3.5.2 trim代替set

<!-- 5.2 if/trim代替set(判断参数) - 将实体类不为空的属性更新 -->  
<update id="updateStudent_if_trim" parameterType="liming.student.manager.data.model.StudentEntity">  
    UPDATE STUDENT_TBL  
    <trim prefix="SET" suffixOverrides=",">  
        <if test="studentName != null and studentName != '' ">  
            STUDENT_TBL.STUDENT_NAME = #{studentName},  
        </if>  
        <if test="studentSex != null and studentSex != '' ">  
            STUDENT_TBL.STUDENT_SEX = #{studentSex},  
        </if>  
        <if test="studentBirthday != null ">  
            STUDENT_TBL.STUDENT_BIRTHDAY = #{studentBirthday},  
        </if>  
        <if test="studentPhoto != null ">  
            STUDENT_TBL.STUDENT_PHOTO = #{studentPhoto, javaType=byte[], jdbcType=BLOB, typeHandler=org.apache.ibatis.type.BlobTypeHandler},  
        </if>  
        <if test="classId != '' ">  
            STUDENT_TBL.CLASS_ID = #{classId},  
        </if>  
        <if test="placeId != '' ">  
            STUDENT_TBL.PLACE_ID = #{placeId}  
        </if>  
    </trim>  
    WHERE STUDENT_TBL.STUDENT_ID = #{studentId}  
</update>  

3.6 choose (when, otherwise)

有时候我们并不想应用所有的条件,而只是想从多个选项中选择一个。而使用if标签时,只要test中的表达式为true,就会执行if标签中的条件。 MyBatis提供了choose 元素。if标签是与(and)的关系,而choose比傲天是或(or)的关系。

choose标签是按顺序判断其内部when标签中的test条件出否成立,如果有一个成立,则choose结束。当choose中所有when的条件都 不满则时,则执行otherwise中的sql。类似于Java 的switch 语句,choose为switch,when为case,otherwise则为default。

例如下面例子,同样把所有可以限制的条件都写上,方面使用。choose会从上到下选择一个when标签的test为true的sql执行。安全考虑,我们使用where将choose包起来,放置关键字多于错误。

<!-- 6 choose(判断参数) - 按顺序将实体类第一个不为空的属性作为where条件 -->  
<select id="getStudentList_choose" resultMap="resultMap_studentEntity" parameterType="liming.student.manager.data.model.StudentEntity">  
    SELECT ST.STUDENT_ID,  
           ST.STUDENT_NAME,  
           ST.STUDENT_SEX,  
           ST.STUDENT_BIRTHDAY,  
           ST.STUDENT_PHOTO,  
           ST.CLASS_ID,  
           ST.PLACE_ID  
      FROM STUDENT_TBL ST   
    <where>  
        <choose>  
            <when test="studentName !=null ">  
                ST.STUDENT_NAME LIKE CONCAT(CONCAT('%', #{studentName, jdbcType=VARCHAR}),'%')  
            </when >  
            <when test="studentSex != null and studentSex != '' ">  
                AND ST.STUDENT_SEX = #{studentSex, jdbcType=INTEGER}  
            </when >  
            <when test="studentBirthday != null ">  
                AND ST.STUDENT_BIRTHDAY = #{studentBirthday, jdbcType=DATE}  
            </when >  
            <when test="classId != null and classId!= '' ">  
                AND ST.CLASS_ID = #{classId, jdbcType=VARCHAR}  
            </when >  
            <when test="classEntity != null and classEntity.classId !=null and classEntity.classId !=' ' ">  
                AND ST.CLASS_ID = #{classEntity.classId, jdbcType=VARCHAR}  
            </when >  
            <when test="placeId != null and placeId != '' ">  
                AND ST.PLACE_ID = #{placeId, jdbcType=VARCHAR}  
            </when >  
            <when test="placeEntity != null and placeEntity.placeId != null and placeEntity.placeId != '' ">  
                AND ST.PLACE_ID = #{placeEntity.placeId, jdbcType=VARCHAR}  
            </when >  
            <when test="studentId != null and studentId != '' ">  
                AND ST.STUDENT_ID = #{studentId, jdbcType=VARCHAR}  
            </when >  
            <otherwise>  
            </otherwise>  
        </choose>  
    </where>    
</select>  

3.7 foreach

对于动态SQL 非常必须的,主是要迭代一个集合,通常是用于IN 条件。List 实例将使用“list”做为键,数组实例以“array” 做为键。

foreach元素是非常强大的,它允许你指定一个集合,声明集合项和索引变量,它们可以用在元素体内。它也允许你指定开放和关闭的字符串,在迭代之间放置分隔符。这个元素是很智能的,它不会偶然地附加多余的分隔符。

注意:你可以传递一个List实例或者数组作为参数对象传给MyBatis。当你这么做的时候,MyBatis会自动将它包装在一个Map中,用名称在作为键。List实例将会以“list”作为键,而数组实例将会以“array”作为键。

这个部分是对关于XML配置文件和XML映射文件的而讨论的。下一部分将详细讨论Java API,所以你可以得到你已经创建的最有效的映射。

3.7.1 参数为array示例的写法

接口的方法声明:

public List<StudentEntity> getStudentListByClassIds_foreach_array(String[] classIds); 

动态SQL语句:

<!— 7.1 foreach(循环array参数) - 作为where中in的条件 -->  
<select id="getStudentListByClassIds_foreach_array" resultMap="resultMap_studentEntity">  
    SELECT ST.STUDENT_ID,  
           ST.STUDENT_NAME,  
           ST.STUDENT_SEX,  
           ST.STUDENT_BIRTHDAY,  
           ST.STUDENT_PHOTO,  
           ST.CLASS_ID,  
           ST.PLACE_ID  
      FROM STUDENT_TBL ST  
      WHERE ST.CLASS_ID IN   
     <foreach collection="array" item="classIds"  open="(" separator="," close=")">  
        #{classIds}  
     </foreach>  
</select>  

测试代码,查询学生中,在20000001、20000002这两个班级的学生:

@Test  
public void test7_foreach() {  
    String[] classIds = { "20000001", "20000002" };  
    List<StudentEntity> list = this.dynamicSqlMapper.getStudentListByClassIds_foreach_array(classIds);  
    for (StudentEntity e : list) {  
        System.out.println(e.toString());  
    } 
}

3.7.2 参数为list示例的写法

接口的方法声明:

public List<StudentEntity> getStudentListByClassIds_foreach_list(List<String> classIdList);  

动态SQL语句:

<!-- 7.2 foreach(循环List<String>参数) - 作为where中in的条件 -->  
<select id="getStudentListByClassIds_foreach_list" resultMap="resultMap_studentEntity">  
    SELECT ST.STUDENT_ID,  
           ST.STUDENT_NAME,  
           ST.STUDENT_SEX,  
           ST.STUDENT_BIRTHDAY,  
           ST.STUDENT_PHOTO,  
           ST.CLASS_ID,  
           ST.PLACE_ID  
      FROM STUDENT_TBL ST  
      WHERE ST.CLASS_ID IN   
     <foreach collection="list" item="classIdList"  open="(" separator="," close=")">  
        #{classIdList}  
     </foreach>  
</select>  

测试代码,查询学生中,在20000001、20000002这两个班级的学生:

@Test  
public void test7_2_foreach() {  
    ArrayList<String> classIdList = new ArrayList<String>();  
    classIdList.add("20000001");  
    classIdList.add("20000002");  
    List<StudentEntity> list = this.dynamicSqlMapper.getStudentListByClassIds_foreach_list(classIdList);  
    for (StudentEntity e : list) {  
        System.out.println(e.toString());  
    }  
}  

五 MyBatis主配置文件

在定义sqlSessionFactory时需要指定MyBatis主配置文件:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
    <property name="configLocation" value="classpath:mybatis-config.xml" />  
    <property name="dataSource" ref="dataSource" />  
</bean>  

MyBatis配置文件中大标签configuration下子标签包括:

configuration

|— properties

|— settings

|— typeAliases

|— typeHandlers

|— objectFactory

|— plugins

|— environments

|— |— environment

|— |— |— transactionManager

|— |— |__ dataSource

|__ mappers

4.1 properties属性

properties和java的.properties的配置文件有关。配置properties的resource指定.properties的路径,然后再在properties标签下配置property的name和value,则可以替换.properties文件中相应属性值。

<!-- 属性替换 -->  
<properties resource="mysql.properties">  
    <property name="jdbc.driverClassName" value="com.mysql.jdbc.Driver"/>  
    <property name="jdbc.url" value="jdbc:mysql://localhost:3306/student_manager"/>  
    <property name="username" value="root"/>  
    <property name="password" value="limingnihao"/>  
</properties>  

4.2 settings设置

这是MyBatis 修改操作运行过程细节的重要的步骤。下方这个表格描述了这些设置项、含义和默认值。

设置项描述允许值默认值
cacheEnabled对在此配置文件下的所有cache 进行全局性开/关设置。true | falsetrue
lazyLoadingEnabled全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。true | falsetrue
aggressiveLazyLoading当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。true | falsetrue
multipleResultSetsEnabled允许和不允许单条语句返回多个数据集(取决于驱动需求)true | falsetrue
useColumnLabel使用列标签代替列名称。不同的驱动器有不同的作法。参考一下驱动器文档,或者用这两个不同的选项进行测试一下。true | falsetrue
useGeneratedKeys允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。true | falsefalse
autoMappingBehavior指定MyBatis 是否并且如何来自动映射数据表字段与对象的属性。PARTIAL将只自动映射简单的,没有嵌套的结果。FULL 将自动映射所有复杂的结果。NONE, PARTIAL, FULLPARTIAL
defaultExecutorType配置和设定执行器,SIMPLE 执行器执行其它语句。REUSE 执行器可能重复使用prepared statements 语句,BATCH执行器可以重复执行语句和批量更新。SIMPLE REUSE BATCHSIMPLE
defaultStatementTimeout设置一个时限,以决定让驱动器等待数据库回应的多长时间为超时正整数Not Set (null)

例如:

<settings>  
    <setting name="cacheEnabled" value="true" />  
    <setting name="lazyLoadingEnabled" value="true" />  
    <setting name="multipleResultSetsEnabled" value="true" />  
    <setting name="useColumnLabel" value="true" />  
    <setting name="useGeneratedKeys" value="false" />  
    <setting name="enhancementEnabled" value="false" />  
    <setting name="defaultExecutorType" value="SIMPLE" />  
</settings>  

4.3 typeAliases类型别名

类型别名是Java 类型的简称。

它仅仅只是关联到XML 配置,简写冗长的JAVA 类名。例如:

<typeAliases>  
    <typeAlias alias="UserEntity" type="com.manager.data.model.UserEntity" />  
    <typeAlias alias="StudentEntity" type="com.manager.data.model.StudentEntity" />  
    <typeAlias alias="ClassEntity" type="com.manager.data.model.ClassEntity" />  
</typeAliases>  

使用这个配置,“StudentEntity”就能在任何地方代替“com.manager.data.model.StudentEntity”被使用。

对于普通的Java类型,有许多内建的类型别名。它们都是大小写不敏感的,由于重载的名字,要注意原生类型的特殊处理。

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

4.4 typeHandlers类型句柄

无论是MyBatis在预处理语句中设置一个参数,还是从结果集中取出一个值时,类型处理器被用来将获取的值以合适的方式转换成Java类型。下面这个表格描述了默认的类型处理器。

类型处理器Java类型JDBC类型
BooleanTypeHandlerBoolean,boolean任何兼容的布尔值
ByteTypeHandlerByte,byte任何兼容的数字或字节类型
ShortTypeHandlerShort,short任何兼容的数字或短整型
IntegerTypeHandlerInteger,int任何兼容的数字和整型
LongTypeHandlerLong,long任何兼容的数字或长整型
FloatTypeHandlerFloat,float任何兼容的数字或单精度浮点型
DoubleTypeHandlerDouble,double任何兼容的数字或双精度浮点型
BigDecimalTypeHandlerBigDecimal任何兼容的数字或十进制小数类型
StringTypeHandlerStringCHAR和VARCHAR类型
ClobTypeHandlerStringCLOB和LONGVARCHAR类型
NStringTypeHandlerStringNVARCHAR和NCHAR类型
NClobTypeHandlerStringNCLOB类型
ByteArrayTypeHandlerbyte[]任何兼容的字节流类型
BlobTypeHandlerbyte[]BLOB和LONGVARBINARY类型
DateTypeHandlerDate(java.util)TIMESTAMP类型
DateOnlyTypeHandlerDate(java.util)DATE类型
TimeOnlyTypeHandlerDate(java.util)TIME类型
SqlTimestampTypeHandlerTimestamp(java.sql)TIMESTAMP类型
SqlDateTypeHandlerDate(java.sql)DATE类型
SqlTimeTypeHandlerTime(java.sql)TIME类型
ObjectTypeHandlerAny其他或未指定类型
EnumTypeHandlerEnumeration类型VARCHAR-任何兼容的字符串类型,作为代码存储(而不是索引)。

你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。要这样做的话,简单实现TypeHandler接口(org.mybatis.type),然后映射新的类型处理器类到Java类型,还有可选的一个JDBC类型。然后再typeHandlers中添加这个类型处理器。

新定义的类型处理器将会覆盖已经存在的处理Java的String类型属性和VARCHAR参数及结果的类型处理器。要注意MyBatis不会审视数据库元信息来决定使用哪种类型,所以你必须在参数和结果映射中指定那是VARCHAR类型的字段,来绑定到正确的类型处理器上。这是因为MyBatis直到语句被执行都不知道数据类型的这个现实导致的。

public class LimingStringTypeHandler implements TypeHandler {  
  
    @Override  
    public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {  
        System.out.println("setParameter - parameter: " + ((String) parameter) + ", jdbcType: " + jdbcType.TYPE_CODE);  
        ps.setString(i, ((String) parameter));  
    }  
  
    @Override  
    public Object getResult(ResultSet rs, String columnName) throws SQLException {  
        System.out.println("getResult - columnName: " + columnName);  
        return rs.getString(columnName);  
    }  
  
    @Override  
    public Object getResult(CallableStatement cs, int columnIndex) throws SQLException {  
        System.out.println("getResult - columnIndex: " + columnIndex);  
        return cs.getString(columnIndex);  
    }  
}  

在配置文件的typeHandlers中添加typeHandler标签。

<typeHandlers>  
    <typeHandler javaType="String" jdbcType="VARCHAR" handler="liming.student.manager.type.LimingStringTypeHandler"/>  
</typeHandlers>  

4.5 ObjectFactory对象工厂

每次MyBatis 为结果对象创建一个新实例,都会用到ObjectFactory。默认的ObjectFactory 与使用目标类的构造函数创建一个实例毫无区别,如果有已经映射的参数,那也可能使用带参数的构造函数。

如果你重写ObjectFactory 的默认操作,你可以通过继承org.apache.ibatis.reflection.factory.DefaultObjectFactory创建一下你自己的。

ObjectFactory接口很简单。它包含两个创建用的方法,一个是处理默认构造方法的,另外一个是处理带参数构造方法的。最终,setProperties方法可以被用来配置ObjectFactory。在初始化你的ObjectFactory实例后,objectFactory元素体中定义的属性会被传递给setProperties方法。

public class LimingObjectFactory extends DefaultObjectFactory {  
  
    private static final long serialVersionUID = -399284318168302833L;  
  
    @Override  
    public Object create(Class type) {  
        return super.create(type);  
    }  
  
    @Override  
    public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {  
        System.out.println("create - type: " + type.toString());  
        return super.create(type, constructorArgTypes, constructorArgs);  
    }  
  
    @Override  
    public void setProperties(Properties properties) {  
        System.out.println("setProperties - properties: " + properties.toString() + ", someProperty: " + properties.getProperty("someProperty"));  
        super.setProperties(properties);  
    }  
  
}  

配置文件中添加objectFactory标签。

<objectFactory type="liming.student.manager.configuration.LimingObjectFactory">  
    <property name="someProperty" value="100"/>  
</objectFactory>  

4.6 plugins插件

MyBatis允许你在某一点拦截已映射语句执行的调用。默认情况下,MyBatis允许使用插件来拦截方法调用:

  • Executor(update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler(getParameterObject, setParameters)
  • ResultSetHandler(handleResultSets, handleOutputParameters)
  • StatementHandler(prepare, parameterize, batch, update, query)

这些类中方法的详情可以通过查看每个方法的签名来发现,而且它们的源代码在MyBatis的发行包中有。你应该理解你覆盖方法的行为,假设你所做的要比监视调用要多。如果你尝试修改或覆盖一个给定的方法,你可能会打破MyBatis的核心。这是低层次的类和方法,要谨慎使用插件。

使用插件是它们提供的非常简单的力量。简单实现拦截器接口,要确定你想拦截的指定签名。

4.7 environments环境

MyBatis 可以配置多个环境。这可以帮助你SQL 映射对应多种数据库等。

4.8 mappers映射器

这里是告诉MyBatis 去哪寻找映射SQL 的语句。可以使用类路径中的资源引用,或者使用字符,输入确切的URL 引用。

例如:

<mappers>  
    <mapper resource="com/manager/data/maps/UserMapper.xml" />  
    <mapper resource="com/manager/data/maps/StudentMapper.xml" />  
    <mapper resource="com/manager/data/maps/ClassMapper.xml" />  
</mappers> 

六 insert操作返回主键

数据库操作怎能少了INSERT操作呢?下面记录MyBatis关于INSERT操作的笔记,以便日后查阅。

一、insert元素 属性详解

其属性如下:

parameterType ,入参的全限定类名或类型别名

keyColumn ,设置数据表自动生成的主键名。对特定数据库(如PostgreSQL),若自动生成的主键不是第一个字段则必须设置

keyProperty ,默认值unset,用于设置getGeneratedKeys方法或selectKey子元素返回值将赋值到领域模型的哪个属性中

useGeneratedKeys ,取值范围 true|false(默认值),设置是否使用JDBC的getGenereatedKeys方法获取主键并赋值到keyProperty设置的领域模型 属性中。MySQL和SQLServer执行auto-generated key field,因此当数据库设置好自增长主键后,可通过JDBC的getGeneratedKeys方法获取。但像Oralce等不支持auto- generated key field的数据库就不能用这种方法获取主键了

statementType ,取值范围STATEMENT,PREPARED(默认值),CALLABLE

flushCache ,取值范围true(默认值)|false,设置执行该操作后是否会清空二级缓存和本地缓存

timeout ,默认为unset(依赖jdbc驱动器的设置),设置执行该操作的最大时限,超时将抛异常

databaseId ,取值范围oracle|mysql等,表示数据库厂家,元素内部可通过<if test="_databaseId = 'oracle'">来为特定数据库指定不同的sql语句

二、一般的INSERT操作——返回值为插入的记录数目

mapper接口代码:

/**
 * 添加学生信息
 * @param student 学生实例
 * @return 成功操作的记录数目
 */
int add(EStudent student);

mapper.xml:

<insert id="add" parameterType="EStudent">
  insert into TStudent(name, age) values(#{name}, #{age})
</insert

三、执行INSERT操作后获取记录主键

mapper接口代码:

/**
 * 添加学生信息
 * @param student 学生实例
 * @return 成功操作的记录数目
 */
int add(EStudent student);

至于mapper.xml则分为两种情况了,一种是数据库(如MySQL,SQLServer)支持auto-generated key field,另一种是数据库(如Oracle)不支持auto-generated key field的。

1. 数据库(如MySQL,SQLServer)支持auto-generated key field的情况

手段①(推荐做法):

<insert id="add" parameterType="EStudent" useGeneratedKeys="true" keyProperty="id">
  insert into TStudent(name, age) values(#{name}, #{age})
</insert>

手段②:

<insert id="add" parameterType="EStudent">
  // 下面是SQLServer获取最近一次插入记录的主键值的方式
  <selectKey resultType="_long" keyProperty="id" order="AFTER">
    select @@IDENTITY as id
  </selectKey>
  insert into TStudent(name, age) values(#{name}, #{age})
</insert>

由于手段②获取主键的方式依赖数据库本身,因此推荐使用手段①。

2. 数据库(如Oracle)不支持auto-generated key field的情况

<insert id="add" parameterType="EStudent">
  <selectKey keyProperty="id" resultType="_long" order="BEFORE">
    select CAST(RANDOM * 100000 as INTEGER) a FROM SYSTEM.SYSDUMMY1
  </selectKey>
  insert into TStudent(id, name, age) values(#{id}, #{name}, #{age})
</insert

注意:mapper接口返回值依然是成功插入的记录数,但不同的是主键值已经赋值到领域模型实体的id中了。

四、 selectKey子元素 详解

作用:在insert元素和update元素中插入查询语句。

其属性如下:

keyProperty ,默认值unset,用于设置getGeneratedKeys方法或selectKey子元素返回值将赋值到领域模型的哪个属性中

resultType ,keyPropety所指向的属性类全限定类名或类型别名

order属性 ,取值范围BEFORE|AFTER,指定是在insert语句前还是后执行selectKey操作

statementType ,取值范围STATEMENT,PREPARED(默认值),CALLABLE

注意:selectKey操作会将操作查询结果赋值到insert元素的parameterType的入参实例下对应的属性中。并提供给insert语句使用

五、批量插入

方式1:

<insert id="add" parameterType="EStudent">
  <foreach collection="list" item="item" index="index" separator=";">
    INSERT INTO TStudent(name,age) VALUES(#{item.name}, #{item.age})
  </foreach>
</insert>

上述方式相当语句逐条INSERT语句执行,将出现如下问题:

  1. mapper接口的add方法返回值将是最一条INSERT语句的操作成功的记录数目(就是0或1),而不是所有INSERT语句的操作成功的总记录数目

  2. 当其中一条不成功时,不会进行整体回滚。

方式2(仅限于MSSQL):

<insert id="add" parameterType="EStudent">
  WITH R AS
  <foreach collection="list" item="item" index="index" open="(" close=")" separator="union all">
    SELECT #{item.name} as a, #{item.age} as b
  </foreach>
  INSERT INTO TStudent(name,age) SELECT a, b FROM R
</insert>

上述方式解决了方式1中的问题。但该方式仅限于MSSQL

方式3(通用解决方法)方式3(MSSQL):

 INSERT INTO TStudent(name,age) 
  <foreach collection="list" item="item" index="index" open="(" close=")" separator="union all">
    SELECT #{item.name} as a, #{item.age} as b
  </foreach>

该方式与方式2效果一样,若为Oracle则采用

INSERT INTO TStudent(name,age) 
  <foreach collection="list" item="item" index="index" open="(" close=")" separator="union all">
    SELECT #{item.name} as a, #{item.age} as b FROM DUAL
  </foreach>

六、总结

关于MyBatis的INSERT操作就总结到这里吧。

尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/4078659.html _肥仔John

七、参考

http://mybatis.github.io/mybatis-3/zh/dynamic-sql.html

七 mybatis各种数据库的批量修改

MyBatis的update元素的用法与insert元素基本相同,因此本篇不打算重复了。本篇仅记录批量update操作的sql语句,懂得SQL语句,那么MyBatis部分的操作就简单了。

注意:下列批量更新语句都是作为一个事务整体执行,要不全部成功,要不全部回滚。

一、MSSQL的SQL语句

WITH R ASSELECT 'John' as name, 18 as age, 42 as  id
  UNION ALL
  SELECT 'Mary' as name, 20 as age, 43 as  id
  UNION ALL
  SELECT 'Kite' as name, 21 as age, 44 as  id
)
UPDATE TStudent SET name = R.name, age = R.age
FROM R WHERE R.id = TStudent.Id

二、MSSQL、ORACLE和MySQL的SQL语句

UPDATE TStudent SET Name = R.name, Age = R.age
from (
SELECT 'Mary' as name, 12 as age, 42 as id
union all
select 'John' as name , 16 as age, 43 as id
) as r 
where ID = R.id

三、SQLITE的SQL语句

当条更新:

REPLACE INTO TStudent(Name, Age, ID)
VALUES('Mary', 12, 42)

批量更新:

REPLACE INTO TStudent(Name, Age, ID)
SELECT * FROM (
 select 'Mary' as a, 12 as b, 42 as c
 union all
 select 'John' as a, 14 as b, 43 as b
) AS R

说明:REPLACE INTO会根据主键值,决定执行INSERT操作还是UPDATE操作。

四、总结

本篇突出MyBatis作为半自动ORM框架的好处了,全手动操控SQL语句怎一个爽字了得。但对码农的SQL知识要求也相对增加了不少,倘若针对项目要求再将这些进行二次封装那会轻松比少。

尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/4078999.html _肥仔John

五、参考

http://feng10251225.blog.163.com/blog/static/6312279820114302391320/

http://www.cnblogs.com/liping13599168/archive/2011/05/24/2054908.html

MyBatis之一对多映射查询sql配置文件

学生—文章的模型
一对多模型

学生student.java类

package com.bjsxt.sxf.po;

import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * 学生
* @ClassName: Student
* @Description: TODO(这里用一句话描述这个类的作用)
* @author 尚晓飞
* @date 2014-11-4 上午10:41:19
*
 */
public class Student {
    private Integer studId;//主键id
    private String name;//姓名
    private String email;//email
    private Date dob;//入学时间
    private List<Article> articles;//文章集合
        //set get 方法  空构造
 }

文章Article.java类

package com.bjsxt.sxf.po;
/**
 * 文章
* @ClassName: Article
* @Description: TODO(这里用一句话描述这个类的作用)
* @author 尚晓飞
* @date 2014-11-4 上午10:41:07
*
 */
public class Article {
    private Integer id;//id
    private String title;//标题
    private String content;//内容
    private Student student;//该文章是哪个学生写的

    //set get 方法 空构造
}

student.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">
<!-- sql集的命名空间,是于数据库交互的桥梁.namespace对应的是映射器的java接口 -->
<mapper namespace="com.bjsxt.sxf.mapper.StudentMapper">


<!--根据id获取学生的基本信息 -->
<!-- 当用resultType时返回单个对象,数据库列名和java类属性名必须一致,如不一致,则在sql语句中起别名,使得列名和属性名一致 -->
<select id="findStudentById" parameterType="int" resultType="Student">
    SELECT students.stud_id as studId,students.`name`,students.email,students.dob FROM students WHERE students.stud_id=#{id}
</select>


<!-- 一对多查询 -->

<!-- resultMap嵌套。查询学生信息时,并将每个学生所写文章的集合也查询出来,赋值在学生对象中的文章集合类中 -->
<!-- 由于resultMap中已经将java类中的属性名和表中的列名,进行映射配置。此处不可为列名起别名 -->
<!-- 文章模型的映射结果 -->
<resultMap type="Article" id="wenzhang">
        <id property="id" column="id" />
        <result property="title" column="title" />
        <result property="content" column="content" />
</resultMap>
<!-- 学生的映射结果 -->
<resultMap type="Student" id="StudentResult">
        <id property="studId" column="stud_id" />
        <result property="name" column="name" />
        <result property="email" column="email" />
        <result property="dob" column="dob" />
        <collection property="articles" javaType="ArrayList" column="stud_id" ofType="Article" resultMap="wenzhang"></collection>
        <!-- ofType是文章集合中的文章类型,column用查处出来的那个列的值,作为外键去查集合结果 -->
</resultMap>
<!-- 根据id获取学生对象,包含该id学生所写的文章集合,可能有的学生没有写文章,因此多表连接查询用左连接-->
<select id="findByIdContentArticle" parameterType="int" resultMap="StudentResult">
    SELECT st.stud_id ,st.`name`,st.email,st.dob,ar.id,ar.title,ar.content FROM students st LEFT JOIN article ar ON (st.stud_id=ar.stuid) WHERE st.stud_id=#{id}
</select>
<!-- 查询出学生的集合,每个学生对象中包含该学生的文章集合 -->
<select id="findByQT" parameterType="int" resultMap="StudentResult">
    SELECT st.stud_id ,st.`name`,st.email,st.dob,ar.id,ar.title,ar.content FROM students st LEFT JOIN article ar ON(st.stud_id=ar.stuid)
</select>



<!-- select嵌套查询 -->
<!-- 文章的映射 -->
<resultMap type="Article" id="wen">
        <id property="id" column="id" />
        <result property="title" column="title" />
        <result property="content" column="content" />
</resultMap>
<!-- 根据学生id查询出文章集合 -->
<select id="findByStudId" parameterType="int" resultMap="wen">
    SELECT ar.id,ar.title,ar.content FROM article ar WHERE ar.stuid=#{id}
</select>
<resultMap type="Student" id="studentsd">
    <id property="studId" column="stud_id" />
        <result property="name" column="name" />
        <result property="email" column="email" />
        <result property="dob" column="dob" />
        <collection property="articles" column="stud_id" javaType="ArrayList" ofType="Article" select="findByStudId"></collection>
        <!-- select当执行完查询学生的sql后再执行根据学生id查文章的sql -->
</resultMap>
<!-- select嵌套查询,查询出指定id的学生(包含学生的文章集合) -->
<select id="findByStudIdS" parameterType="int" resultMap="studentsd">
    SELECT st.stud_id,st.`name`,st.email,st.dob FROM students st where st.stud_id=#{id}
</select>
<!-- select嵌套查询  ,查询出所有的学生集合(每个学生对象中包含该学生的文章集合)-->
<select id="findBySelectList" resultMap="studentsd">
    SELECT st.stud_id,st.`name`,st.email,st.dob FROM students st
</select>

</mapper>

Article.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">
<!-- sql集的命名空间,是于数据库交互的桥梁.namespace对应的是映射器的java接口 -->
<mapper namespace="com.bjsxt.sxf.mapper.ArticleMapper">


<!-- 查询出指定id的文章 -->
<select id="findArticleById" parameterType="int" resultType="Article">
    SELECT article.id,article.title,article.content FROM article where article.id=#{id}
</select>
<!-- MyBatis的灵活处,也在此。需要什么样的结果集,就配置什么样的映射 -->
<resultMap type="Article" id="ArticleOnly">
        <id property="id" column="id" />
        <result property="title" column="title" />
        <result property="content" column="content" />
</resultMap>
<!-- 查询出文章的集合,只是基本信息 -->
<select id="findArticleOnly" resultMap="ArticleOnly">
    SELECT id,title,content FROM article
</select>


<!-- 多对一的查询 -->

<!--ResultMap嵌套查询  -->
<!-- 查询文章时,并将文章的作者学生信息也查询出来 -->
<resultMap type="Student" id="studentRe">
        <id property="studId" column="stud_id" />
        <result property="name" column="name" />
        <result property="email" column="email" />
        <result property="dob" column="dob" />
</resultMap>
<resultMap type="Article" id="ArticleResult">
        <id property="id" column="id" />
        <result property="title" column="title" />
        <result property="content" column="content" />
        <association property="student" resultMap="studentRe"></association>
</resultMap>
<!-- 根据文章id获取文章信息,并将该文章的作者也查询出来 -->
<select id="findArticleWithStudentById" parameterType="int" resultMap="ArticleResult">
    SELECT ar.id,ar.title,ar.content,st.stud_id as studId,st.`name`,st.email,st.dob FROM article ar,students st WHERE ar.stuid=st.stud_id AND ar.id=#{id}
</select>

<!-- 查询出文章的集合,并将每篇文章的作者也查询出来 -->
<select id="findAllArticle" resultMap="ArticleResult">
    SELECT ar.id,ar.title,ar.content,st.stud_id as studId,st.`name`,st.email,st.dob FROM article ar,students st WHERE ar.stuid=st.stud_id
</select>



<!-- select嵌套查询 -->
<!-- 根据学生id查询出学生的信息 -->
<select id="findStudentByStuid" parameterType="int" resultType="Student">
    SELECT st.stud_id as studId,st.`name`,st.email,st.dob FROM students st WHERE st.stud_id=#{id}
</select>
<resultMap type="Article" id="as">
        <id property="id" column="id" />
        <result property="title" column="title" />
        <result property="content" column="content" />
        <association property="student" javaType="Student" column="stuid" select="findStudentByStuid"></association>
</resultMap>
<!-- 根据select嵌套查询出指定id的文章,并将文章的信息也查询出来 -->
<select id="findArticleBySelect" parameterType="int" resultMap="as">
    SELECT * FROM article ar where ar.id=#{id}
</select>
<!-- 根据select嵌套查询出文章的集合,每篇文章的作者也查询出来 -->
<select id="findAllBySelect" resultMap="as">
    SELECT * FROM article
</select>
</mapper>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值