Mybatis学习笔记  部分

Mybatis学习笔记 部分

Scroll Down

MyBatis

1、简介

1.1 什么是MyBatis

MyBatis是一款优秀的持久层框架,支持定制化SQL、存储过程以及高级映射。Mybatis免除几乎所有JDBC代码以及设置参数和获取结果集的工作。MyBatis可以通过简单的XML或注解来配置和映射原始类型、结构和Java Pojo (Plain old java objects普通老式Java对象)为数据库中的记录。

1.2 持久化

用于将数据持久化

  • 持久化就是

1.3 持久层

DAO层,Service层,Controller层。

  • 完成持久化工作的代码块
  • 层界限十分明显

1.4 为什么需要Mybatis

  • 帮助程序员将数据存入到数据库
  • 方便
  • 传统JDBC代码过于复杂,需要简化,框架,自动化
  • 更容易上手
  • SQL和代码分离

最重要的: 应用广泛

2、第一个Mybatis程序

2.1 搭建环境

  • 搭建数据库
  • 创建数据库
create table `user` (
  `id` int(20) not null auto_increment,
  `name` varchar(30) DEFAULT null,
  `pwd` varchar(30) default null,
  primary key (`id`)
)engine=innodb default charset=utf8;

insert into `user`(`id`, `name`, `pwd`) values
(1, '狂神', '123456'),
(2, '张三', '123456'),
(3, '李四', '123890')

项目依赖:

  • mysql驱动(jdbc) 8.0.18
  • mybatis 3.5
  • junit 4.12

2.2 创建module

  • 编写mybatis核心配置文件 (configuration 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 mybatis核心配置文件-->
    <configuration>
    <!--    环境有dev和test两种-->
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
    
    <!--            数据源-->
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
    
    </configuration>
    
  • 构建SQLSessionFactory 并从中获取SqlSession

    SqlSession包含与sql数据库交互的所有方法

  • 编写mybatis工具类

    public class MybatisUtils {
    
        private static SqlSessionFactory sqlSessionFactory;
        static {
    
            try {
                // 获取SqlSessionFactory对象
                String resource = "mybatis-config.xml";
                InputStream inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static SqlSession getSqlSession() {
            // 有了sqlSessionFactory后,就可以从中得到SqlSession实例
            // SqlSession 完全包含了面向数据库执行的SQL命令所需的所有方法
            return sqlSessionFactory.openSession();
        }
    }
    
    

2.3 编写代码

  • 数据库操作-->实体类

    public class User {
        private int id;
        private String name;
        private String pwd;
    
        public User() {
        }
    
        public User(int id, String name, String pwd) {
            this.id = id;
            this.name = name;
            this.pwd = pwd;
        }
    
        @Override
        public int hashCode() {
            return super.hashCode();
        }
    
        @Override
        public boolean equals(Object obj) {
            return super.equals(obj);
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        @Override
        public String toString() {
            return super.toString();
        }
    
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
        }
    }
    
  • DAO接口

    package cn.nolaurence.dao;
    
    import cn.nolaurence.pojo.User;
    
    import java.util.List;
    
    public interface UserDao {
        // namely mapper
        //DAO means database access object
        List<User> getUserList();
    }
    
  • 接口实现类:由JDBNBC的UserDaoImpl转变成为一个Mapper配置文件

    <?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">
    <!--通过namespace绑定Dao/Mapper对象-->
    <mapper namespace="cn.nolaurence.dao.UserDao">
    <!--    查询语句-->
      <select id="getUserList" resultType="cn.nolaurence.pojo.User">
        select * from mybatis.user
      </select>
    </mapper>
    

2.4 测试

point:Exception

org.apache.ibatis.binding.BindingException: Type interface cn.nolaurence.dao.UserDao is not known to the MapperRegistry.

MapperRegistry

核心配置文件中加入xml:

<mappers>
    <mapper resource="cn/nolaurence/dao/Usermapper.xml" />
</mappers>
  • 测试

    public class UserDaoTest {    
        @Test    
        public void test() {        
            // 获取sqlSession对象        
            SqlSession sqlSession = MybatisUtils.getSqlSession();        
            // 执行sql  method1: getMapper        
            UserDao mapper = sqlSession.getMapper(UserDao.class);        
            List<User> userList = mapper.getUserList();        
            for (User user : userList) {            
                System.out.println(user);        
            }        
            sqlSession.close();   
        }
    }
    
  • Point获取方式有两种 推荐第一

    public class UserDaoTest {    
        @Test    
        public void test() {        
            // 获取sqlSession对象       
            SqlSession sqlSession = MybatisUtils.getSqlSession();        
            // method1        
            // 执行sql  method1: getMapper        
            UserDao mapper = sqlSession.getMapper(UserDao.class);        
            List<User> userList = mapper.getUserList();        
            // method2//        
            List<User> userList = sqlSession.selectList("cn.nolaurence.dao.UserDao.getUserList");        
            for (User user : userList) {            
                System.out.println(user);        
            }        
            sqlSession.close();    
        }
    }
    
  • 最重要的三个类

    SqlSessionBuilder

    SqlSessionFactory

    SqlSession

    Point:

    • SqlSessionFactoryBuilder可以被实例化、使用和丢弃,一旦创建了SqlSessionFactory就不需要使用它了

    • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。因此,SqlSessionFactory的最佳作用域是应用作用域,最简单的方法是使用单例模式或者静态单例模式。

    • SqlSession的实例不是现成安全的,因此不能被共享。换句话说,每次收到的HTTP请求,就可以打开一个SqlSession,返回一个响应就关闭它。关闭操作是很重要的。

3. CRUD

1. namespace

xml中的namespace包名应当与实际代码一致

2. select 查询语句

<select id="getUserList" resultType="cn.nolaurence.pojo.User">        
    select * from mybatis.user
</select>
  • id:对应的namespace 接口中的方法名
  • resultType:SQL语句执行的返回值
  • parameterType:参数类型

3. 增删改

insert, update, delete

通用:

  • 编写接口

    public interface UserDao {    
        // namely mapper    
        //DAO means database access object    
        List<User> getUserList();    
        //insert a user    
        int addUser(User user);    
        // update    
        int updateUser(User user);
    }
    
  • 编写对应的mapper中的sql语句

    <mapper namespace="cn.nolaurence.mapper.UserDao">
        <!--    select statement-->  
        <select id="getUserList" resultType="cn.nolaurence.pojo.User">    
            select * from mybatis.user  
        </select><!--  对象中的属性可以直接取出来-->  
        <insert id="addUser" parameterType="cn.nolaurence.pojo.User">    
            insert into mybatis.user (id, name, pwd) values (#{id}, #{name}, #{pwd})  
        </insert>
        <!--  update-->  
        <update id="updateUser" parameterType="cn.nolaurence.pojo.User">    
            update mybatis.user set `name`=#{name},pwd=#{pwd} where id = #{id}  
        </update>
    </mapper>
    
  • 测试

    @Test    
    public void addUser() {        
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {            
            UserDao mapper = sqlSession.getMapper(UserDao.class);            
            int number = mapper.addUser(new User(4, "你好", "1255555"));            
            // 增删改需要提交事务Transaction            
            sqlSession.commit();            
            // commit非常重要        
        }    
    }
    

    Tip: 赠删改需要commit

Map

假设表中的字段或者参数过多,应该考虑使用Map;

// insert use mao    
int addUser2(Map<String, Object> map);
<insert id="addUser" parameterType="map">    
    insert into mybatis.user (`id`, `name`, `pwd`) values (#{userid}, #{userName}, #{passWord})  
</insert>
@Test    
public void getUserById2() {        
    try(SqlSession sqlSession = MybatisUtils.getSqlSession()) {            
        UserDao mapper = sqlSession.getMapper(UserDao.class);            
        Map<String, Object> map = new HashMap<>();            
        map.put("id", 1);            
        User user = mapper.getUserById2(map);            
        System.out.println(user);        
    }    
}

Map传递参数,直接在sql中取出key即可 【parameterType="map"】

对象传递参数,直接在sql中取出对象的属性 【parameterType=“Object”】

只有一个基本类型参数的情况下,使用Map可以直接在sql中取到

4. 配置解析

1. 核心配置文件

  • mybatis-config.xml

  • Point: xml属性的排列顺序有严格的要求

  • Mybatis的配置文件内容:

    configuration  配置
    properties     属性
    settings       设置
    typeAliases    类型别名
    typeHandlers   类型处理器
    objectFactory  对象工厂
    plugins        插件
    environments   环境配置
    environment    环境变量
    transactionManager   业务管理器
    dataSource     数据源
    databaseIdProvider  数据库厂商标识
    mappers        映射器
    

2. 环境变量

  • transactionManager: JDBC和MANAGED

MANAGED:这个配置几乎没做什么,它从来不提交或回滚一个连接,而是让容器来管理是事物的整个生命周期(eg JEE的server contex)。默认情况下他会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置为false来组织它的默认关闭行为。例如:

<transactionManager type="MANAGED">    
    <property name="closeConnection" value="false"/>
</transactionManager>
  • 数据源 dataSource

    连接数据库 dpcb c3p0 druid

    内建的数据源类型,UNPOOLED POOLED JNDI

    • UNPOOLED 性能要求不高
    • JNDI
    • POOLED:使响应速度加快。

Mybatis默认的事务管理器是JDBC 默认的数据源是POOLED

3. 属性 properties

我们可以通过properties属性来实现引用配置文件

这些属性都是可外部配置且可动态替换的,你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。例如:

编写一个配置文件 db.properties

driver=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306/mybatis?useSSL=true&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8username=rootpassword=123456

在xml中注册 引入

<properties resource="org/mybatis/example/config.properties">  
    <property name="username" value="dev_user"/>  
    <property name="password" value="F2Fa3!33TYyg"/>
</properties>

除了直接引入properties文件中的变量,还能增加一些属性配置,如上

优先级:如果字段相同,优先使用外部配置

4. 设置 settings

会改变Mybatis的运行方式

5. 类型别名 typeAlias

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写,例如:

<typeAliases>  
    <typeAlias alias="Author" type="domain.blog.Author"/>  
    <typeAlias alias="Blog" type="domain.blog.Blog"/>  
    <typeAlias alias="Comment" type="domain.blog.Comment"/>  
    <typeAlias alias="Post" type="domain.blog.Post"/>  
    <typeAlias alias="Section" type="domain.blog.Section"/>  
    <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

也可以针对一个包指定一个别名,Mybatis会在包名下搜索需要的Java Bean

默认别名就是类型的小写形式(首字母变为小写)。若有注解,则别名为其注解名字

<typeAliases>	
    <package name="cn.nolaurence.pojo"/>
</typeAliases>

第二种方法如果需要改Alias,就在实体类上加注解

下面是一些为常见的 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

6. 其他

typeHandlers objectFactory plugins不需要了解

7. 映射器 mappers

MapperRegistry:注册绑定我们的mapper文件

  • 方式一:

    <!-- 使用相对于类路径的资源引用 -->
    <mappers>  
        <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>  
        <mapper resource="org/mybatis/builder/BlogMapper.xml"/>  
        <mapper resource="org/mybatis/builder/PostMapper.xml"/>
    </mappers>
    
  • 方式二:

    <!-- 使用映射器接口实现类的完全限定类名 -->
    <mappers>  
        <mapper class="org.mybatis.builder.AuthorMapper"/>  
        <mapper class="org.mybatis.builder.BlogMapper"/>  
        <mapper class="org.mybatis.builder.PostMapper"/>
    </mappers>
    
  • 方式三:

    <!-- 将包内的映射器接口实现全部注册为映射器 -->
    <mappers>  
        <package name="org.mybatis.builder"/>
    </mappers>
    
  • 注意点:

    • 接口和它的Mapper配置文件必须同名
    • 接口和它的Mapper配置文件

8. 生命周期和作用域

image-20210527092205664

生命周期和作用域非常重要,错误使用会导致严重的并发问题

SqlSessionFactoryBuilder

  • 一旦创建了SqlSessionFactory,就不需要它了
  • 局部变量

SqlSessionFactory

  • 说白了就是可以想象为:数据库连接池
  • SqlSessionFactory一旦被创建就应该在应用运行期间一直存在,没有任何理由丢弃它或重新创建一个实例
  • 作用域最好是全局。
  • 最简单的就是使用单例模式或者静态单例模式

SqlSession

  • 连接到连接池的一个请求
  • SqlSession实例不是线程安全的,因此用完后就需要赶紧关闭。

image-20210527092426260

每一个Mapper对应一个具体的业务

5. ResultMap

1. 要解决的问题

解决属性名和数据库字段不一致的问题

实体类中 密码为 password 数据库中为 pwd

image-20210527095841002

解决方法:

  • 起别名:

    select id, `name`, pwd as password where id = #{id}
    

2. resultMap

结果集映射

id	name	pwdid	name	password
<resultMap id="UserMap" type="User">
    <!--        column是数据库中的字段, property是实体类中的属性-->        
    <result column="id" property="id"/>        
    <result column="name" property="name"/>        
    <result column="pwd" property="password"/>
</resultMap>
  • resultMap元素是Mybatis中最重要最强大的元素
  • ResultMap的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂疑点的语句,只需要描述他们的关系就行
  • 虽然你对它相当了解,但是你不需要显示的去调用它

6. 日志

6.1 日志工程

如果一个数据库操作出现了一场,我们需要拍错,那么最好使用日志

previous:sout、debug

now:日志工厂

  • SLF4J
  • LOG4J 掌握
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING 掌握
  • NO_LOGGING

具体使用在mybatis中设置。

<settings>        
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

6.2 log4j

Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件

我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

  1. 导入依赖
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>    
    <groupId>log4j</groupId>    
    <artifactId>log4j</artifactId>    
    <version>1.2.17</version>
</dependency>
  1. 配置文件
log4j.rootLogger=DEBUG,console,
filelog4j.appender.console = org.apache,
log4j.ConsoleAppenderlog4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
log4j.appender.file = org.apache.log4j.RollingCalendar
log4j.appender.file.File=./log/nolaurence.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
  1. 配置mybatis为LOG4J实现
<settings>        
    <setting name="logImpl" value="LOG4J"/>
</settings>

简单使用

  1. 在要使用Log4j的类中,导入包 import org.apache.log4j.Logger;

  2. 日志对象,参数为当前类的加载对象:

    static Logger logger = Logger.getLogger(UserMapperTest.class);
    
  3. 日志级别

    info debug error

7. 分页

7.1 使用Limit分页

SELECT * FROM `user` 3;  -- [0, n]单参数

使用Mybatis实现分页,核心sql语句:

  1. 接口

    List<User> getUserByLimit(Map<String, Integer> map);
    
  2. Mapper.xml

    <select id="getUserByLimit" parameterType="map" resultType="User">    
        select * from mybatis.user limit #{startIndex},#{pageSize}
    </select>
    
  3. 测试

    @Test
    public void getUserByLimit() {    
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {        
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);        
            HashMap<String, Integer> queryMap = new HashMap<>();        
            queryMap.put("startIndex", 0);        
            queryMap.put("pageSize", 2);        
            List<User> userList = mapper.getUserByLimit(queryMap);        
            for (User user : userList) {            
                System.out.println(user);        
            }    
        }
    }
    

7.2 RowBounds分页

不使用SQL实现分页(一般推荐直接用SQL)

  1. 接口

    List<User> getUserByRowBounds();
    
  2. mapper.xml

    <select id="getUserByRowBounds" resultMap="UserMap">
        select * from mybatis.users
    </select>
    
  3. 测试

7.3 分页插件

PageHelper 了解

8、使用注解开发

8.1、面向接口编程

根本原因:解耦、可扩展、提高复用、分层开发中,上层不用管具体的实现,大家都遵守共同的标准,是的开发变得的容易、规范

接口的意义

  • 定义与实现的分离
  • 接口的本身反应了系统设计人员对系统的抽象理解
  • 一个题可能有多个抽象面,抽象体和抽象面是有区别的

Sql方法原理

  1. Resources获取加载全局配置文件
  2. 实例化SqlSessionFactoryBuilder构造器
  3. 解析文件流XMLConfigBuilder
  4. Configuration 所有的配置信息
  5. SqlSessionFactory 实例化
  6. Transaction 事务管理
  7. executor执行
  8. 创建sqlSession
  9. 实现CRUD
  10. 事务提交

8.2 使用注解开发

9、Lombok

描述:Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.

Never write another getter or equals method again, with one annotation your class has a fully featured builder, automate your logging variables, and much more.

可以添加的注解:

@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor
@RequiredArgsConstructor
@NoArgsConstructor
@Log @Log4j  @Log4j2 @slf4j  @xslf4j @CommonsLog @JBossLog  @Flogger
@Data
@Builder
@Singular
@Delegate
@Value
@Accessors
@Wither
@SneakyThrows

10、多对一处理

测试环境搭建

  1. 导入Lombok
  2. 新建实体类
  3. 建立Mapper接口
  4. 建立Mapper.xml文件
  5. 在核心配置文件中注册Mapper接口或者文件
  6. 测试查询是否成功

按照查询嵌套处理

<select id="getStudent" resultMap="studentTeacher">
	select * from student
</select>

<resultMap id="studentTeacher" type="student">
	<result property="id" column="id"/>
    <result property="name" column="name"/>
    <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>

<select id="getTeacher" resultType="Teacher">
	select * from teacher where id = #{id}
</select>

按照结果嵌套处理

<select id="getStudent2" resultMap="StudentTeacher2">
    select s.id sid, s.name sname, t.name tname from student s, teacher t where s.tid = t.id;
</select>

<resultMap id="StudentTeacher2" type="Student">
    <result property="id" column="sid"/>
    <result property="name" column="sname"/>
    <association property="name" javaType="Teacher">
    	<result property="name" column="tname"/
    </association>
</resultMap>

11、动态SQL

动态SQL主要包含四个命令:

  • if
  • choose ( when, otherwise )
  • trim (where, set)
  • foreach

if

使用动态SQL最常见情景是根据条件包含where子句的一部分。

<select id="findActiveBlogWithTitleLike" resultType="Blog">
	SELECT * FROM BLOG WHERE state = 'ACTIVE'
    <if test="title != null">
    	AND title like #{title}
    </if>
</select>
<select id="findActiveBlogLike" resultType="Blog">
	SELECT * FROM BLOG WHERE state = 'active'
    <if test="title != null">
    	AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
    	AND author_name like #{author.name}
    </if>
</select>

choose、when、otherwise

类似于switch

<select id="findActiveBlogLike" resultType="Blog">
	SELECT * FROM BLOG WHERE state = "ACTIVE"
    <choose>
    	<when test="title != null">
        	AND title like #{title}
        </when>
        <when test="author != null and author.name != null">
        	AND author_name like #{author.name}
        </when>
        <otherwize>
        	AND featured = 2
        </otherwize>
    </choose>
</select>

trim、where、set

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容并且插入 prefix 属性中指定的内容

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

<update id="updateAuthorIfNecessary">
  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}
</update>

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

来看看与 set 元素等价的自定义 trim 元素吧:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

注意,我们覆盖了后缀值设置,并且自定义了前缀值。

foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

至此,我们已经完成了与 XML 配置及映射文件相关的讨论。下一章将详细探讨 Java API,以便你能充分利用已经创建的映射配置。

多数据库支持

如果配置了 databaseIdProvider,你就可以在动态代码中使用名为 “_databaseId” 的变量来为不同的数据库构建特定的语句。比如下面的例子:

<insert id="insert">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    <if test="_databaseId == 'oracle'">
      select seq_users.nextval from dual
    </if>
    <if test="_databaseId == 'db2'">
      select nextval for seq_users from sysibm.sysdummy1"
    </if>
  </selectKey>
  insert into users values (#{id}, #{name})
</insert>