mybatis
1.MyBatis简介
1.1 什么是MyBatis
- MyBatis是一款优秀半自动化的持久层框架,轻量级框架
- 他支持定制SQL,存储过程以及高级映射
- MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
- MyBatis可以使用简单的XML或注解来配置和映射原生类型,接口和JAVA的POJO为数据库中的记录。
1.2 MyBatis与Hibernate,JDBC的区别
JDBC:执行过程 SQL夹在Java代码块里,耦合度高导致硬编码
维护不易且实际开发需求中的sql是又变化,频繁修改的情况多。
Hibernate:长度复杂SQL,对于Hibernate处理也不容易, 内部自动产生的SQL,不容易做特殊优
化,基于全映射的自动框架,大量字段的POJO进行部分映射时比较困难,导致数据库性能下降。
对于开发人员而言,核心SQL还是需要自己优化。
Sql和java编码分开,功能边界清晰,一个专注业务,一个专注数据。
1.3 MyBatis使用步骤
- 创建maven项目,添加依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
- resource下创建配置文件 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver"
value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/easybuy?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers> <!--映射-->
<mapper resource="com/it/mapper/DepartmentMapper.xml"/>
</mappers>
</configuration>
- 创建pojo对象 创建mapper接口 以及映射文件 xxMapper文件 【约定大于配置】 mapper文件和接口保持一致
mapper中定义的statement必须和接口中的方法保持一致
<!-- mappers
将我们写好的sql映射文件注册到全局配置文件中
mapper:注册一个sql映射
resource:引用类路径下的sql映射文件
url:引用网络路径或磁盘路径下的sql映射文件 file://xxxx 不常用
class:引入注册接口。有sql映射文件,映射文件名必须和接口同名,必须放在与
接口同一目录下
如果没有sql映射文件,可以写注解的方式。
package:批量注册,此时要求配置文件和接口必须再同包下
-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.it.mapper.DepartmentMapper">
<!-- public Department selectById(int id);-->
<select id="selectById" resultType="com.it.pojo.Department">
select * from department where departid=#{id}
</select>
</mapper>
1.3.4 测试代码
/**
* 1.读取配置文件的信息,获取文件的流。
* 2.创建sqlSessionFactory:根据xml配置文件创建改对象【包含了数据源等运行环
境的信息】
* 3.通过sqlSessionFactory获取session,session就代表和数据库的一次会话
* 4.通过session来完成对数据库的操作
* 5.关闭session
*
*
* mybatis有两个文件,一个是配置文件:config 该文件中定义一些关于数据源等运
行环境需要的配置信息
* 一个是映射文件:mapper 映射文件,配置了数据库于java的映
射
*/
@Test
public void testOne() {
try {
InputStream inStream = Resources.getResourceAsStream("mybatisconfig.xml");
SqlSessionFactory factory = new
SqlSessionFactoryBuilder().build(inStream);
SqlSession session = factory.openSession();
Person person = (Person)session.selectOne("selectOne",1);
System.out.println(person.getPid()+"\t"+person.getPname()+"\t"+person.getPaddr()+"\t"+person.getPsex()+"\t"+person.getPbirthday());
session.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
2、深入了解配置文件
1 、了解Config配置文件
1> properties标签
自定义properties文件,将关于数据库的配置放入该文件中。然后数据源引入该文件properties文
件中所定义的配置
properties:属性有俩,一个是resource:用于执行本项目中的配置文件,也可以 是url:一般引入本地资源文件或网络资源文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration SYSTEM "http://mybatis.org/dtd/mybatis-3-config.dtd"
>
<configuration>
<properties resource="jdbc.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="jdbc"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
……
</dataSource>
</environment>
</environments>
……
</configuration>
2>settings标签
<!--开启驼峰命名规则-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
3>typeAliases标签 别名处理器。
将java类型起简短的别名。(别名不区分大小写)
<typeAliases>
<typeAlias type="com.bd.www.model.Dept" alias="Dept"/>
</typeAliases>
将Dept类起别名Dept,如果省略alias,默认的别名类名小写。
<typeAliases>
<package name="com.bd.www.model"/>
</typeAliases>
除此以为还可以批量起别名(别名为默认值),使用package标签,为该包下的所有的类起别名。Name属
性指定包名。为当前包以及后代包都起一个默认别名,就是类型小写。
如果此时不想使用默认别名或者类名重复,可以在该类中使用注解@Alias("depart")的方式起别名
注意:mybatis有已经起好的别名。自己起的别名不能与人家起好的别名重复。推荐使用全类名
4>typeHandlers标签
java类型与数据库类型进行适配,mybaits3.4后的版本已经自己注册成功了。之前的版本需要自己通过
该标签配置
5>plugins标签
自定义插件,已经定义好的有以下四个
Executor:执行器,增删改查
ParameterHandler:参数处理器,sql预编译需要设置参数
ResultSetHandler:结果集处理器,将查出的数据封装到对象
StatementHandler:sql语句处理器
6>environments标签
可以配置多个环境,有子标签environment,具体配置一个环境。通过environments中的default:属性
指向默认配置的某个环境。
environment标签,有一个id属性,代表当前环境的唯一标识(可以自己取值)。environments中的
default指向该属性值,来表示当前默认的执行环境。该必须再包含俩个子标签,为
transactionManager(配置事物管理器)以及dataSource(数据源)标签。
7>transactionManager标签
该标签是environment标签的子标签。该标签有一个type属性,该属性值的取值有: 1)JDBC:使用jdbc方
式提交回滚事务 2)MANAGED:使用J2EE应用服务器的方式提交管理事务的。这俩值就是俩别名,可以打
开源码,查看Configuration类中找到俩别名所对应的内容。
8>dataSource标签
数据源,其属性type,规定了数据源的类型。该属性有三种取值方式1)UNPOOLED:不使用连接池,每次连
接数据库,将会从数据库中获取新连接。2)POOLED:使用连接池获取数据库连接。3)JNDI。也可以自定
义连接池,如dbcp,c3p0都可以。使用自定义数据源,需要实现DataSourceFactory接口,此时type需
要些自己定义的数据源的全限定类名。
9>databaseIdProvider标签
mybatis根据不同的数据库厂商执行不同的sql。在移植数据库的时候,会使用该标签。使用步骤
1)在config数据文件中添加databaseIdProvider标签。作业就是获取数据库厂商的标识。
<databaseIdProvider type="DB_VENDOR">
<!-- 为不同的数据库厂商起别名 -->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name
2)在mapper映射文件中,在select或update标签中添加databaseId="mysql",此时该标签中的sql语句则在连接mysql数据的时候执行。注意测试的时候,记得更改全局配置文件中的dataSource的配置,以及添加oracle的驱动。
<select id="findOne" resultType="com.bd.www.model.Dept">
select * from dept where deptid=#{id}
</select>
<select id="findOne" resultType="com.bd.www.model.Dept" databaseId="mysql">
select * from dept where deptid=#{id}
</select>
<select id="findOne" resultType="com.bd.www.model.Dept" databaseId="oracle">
select id deptid,name deptname,desc deptdesc from department where id=#
{id}
</select>
3、了解mapper映射文件
1> 使用mybatis完成增删改操作
<insert id="insert" parameterType="com.bd.www.model.Dept">
insert into dept values(null,#{deptname},#{deptdesc});
</insert>
<update id="update">
update dept set deptname=#{deptname} ,deptdesc=#{deptdesc} where deptid=#
{deptid}
</update>
<delete id="delete">
delete from dept where deptid=#{id}
</delete>
这些标签 ,parameterType标签可以省略。没有返回值类型,但是我们在写接口中方法的时候,可以加
上方法的返回值类型,如返回整形或boolean类型。在调用的时候,会根据定义的方法自动返回方法定
义的类型。除此以外,mybatis是事务是手动提交的,所以在写代码的时候,需要通过sqlSession的
commit方法提交事务。或者再通过 sqlSession = sqlSessionFactory.openSession(true);此时的事务
也可以自动提交.
2>主键自动生成策略
1)MySQL获取自增主键
<insert id="insert" parameterType="com.bd.www.model.Dept"
useGeneratedKeys="true" keyProperty="deptid">
insert into dept values(null,#{deptname},#{deptdesc})
</insert>
useGeneratedKeys="true":使用自增主键策略
keyProperty="deptid":将生成的主键绑定到对象的哪个属性中,此时便可以通过对象的deptid属性获
取自增主键的值。
2)Oracel数据库自增
Oracle数据库是借助sequences实现自增的,所以,如果使用Oracle数据库,其代码如下:
<insert id="insert" databaseId="oracle">
<selectKey keyProperty="deptid" order="BEFORE" resultType="Integer">
select dept_seq.nextval from dual
</selectKey>
insert into dept values(#{deptid},#{deptname},#{deptdesc})
</insert>
keyProperty="deptid":讲查询的序列的值存储到deptid属性中
order="BEFORE":运行顺序:
先执行selectKey查询id的sql,查出id值封装给javaBean的ID属性。再运行插入sql,就可以取出id属性对
应的值。
order="AFTER":运行顺序
先执行插入sql语句,然后再执行selectKey
推荐使用BEFORE方式使用,因为如果该语句影响多条,获取最后一条数据的主键值
resultType="Integer":查询序列返回值类型
<insert id="insert" databaseId="orace">
<selectKey keyProperty="deptid" order="AFTER" resultType="Integer">
select dept_seq.currval from dual
</selectKey>
insert into dept values(dept_seq.nextval,#{deptname},#{deptdesc})
</insert>
3>参数处理
1) 单个参数
Mybatis不做任何特殊处理,使用#{参数名}取出参数值,名字随便写。因为只有一个参数
2) 多个参数
Mybatis会做特殊处理,会将多个参数封装成一个map。
Key: param1…paramN
Value: 传入的参数值
#{}就是从map中获取指定的key的值
<!--Dao:中定义的方法public Dept select(int id,String name);
映射文件:-->
<select id="select" resultType="com.bd.www.model.Dept">
select * from dept where deptid=#{param1} and deptname = #{param2}
</select>
3)命名参数
使用注解@Param("id")指定参数名字,此时参数依旧会被封装到map,只是map元素的key的值,不再是
默认的param1而是指定的id的名字
<!--Dao:中定义的方法:
public Dept select2(@Param("id") int id,@Param("name") String name);
映射文件:-->
<select id="select2" resultType="com.bd.www.model.Dept">
select * from dept where deptid=#{id} and deptname = #{name}
</select>
如果多个参数正好是pojo对象,可以直接传入该对象,从该对象中取值。
4) Map传值: 如果多个参数不是pojo对象,可以传入map
<!--Dao:中定义的方法:
public Dept select3(Map<String, Object> map);
映射文件:-->
<select id="select3" resultType="com.bd.www.model.Dept">
select * from dept where deptid=#{id} and deptname = #{name}
</select>
如果当多个值经常使用,但又不是pojo对象中的属性,可以专门定义一个类
5)参数取值举例
public Dept getDept (@Param("id")Integer id,String name);
public Dept getDept(Integer id, Dept dept);
public Dept getDept(Integer id,@Param("d")Dept dept);
##如果是Collection(List,Set)类型或者是数组,也会特殊处理,也是把传入的list或者数组封装到
map中。Key:Collection(collection),List(list),数组(array)
public Dept getDept (@Param("id")Integer id,String name);
取值:id==>#{id/param1} deptname==>#{param2}
public Dept getDept(Integer id, Dept dept);
取值:id==>#{param1} deptname===>#{param2.deptname }
public Dept getDept(Integer id,@Param("d")Dept dept);
取值:id==>#{param1} deptname===>#{param2.deptname/d.deptname}
##如果是Collection(List,Set)类型或者是数组,也会特殊处理,也是把传入的list或者数组封装到
map中。Key:Collection(collection),List(list),数组(array)
4> #{}与${}取值区别
#{}与${}都可以获取map中的值或者pojo对象属性的值
区别:#{}是以预编译的形式,将参数设置到sql语句中。PreparedStatement
${}会将值直接拼接到sql语句中。会存在sql注入的安全问题。所以一般情况下,使用#{}方式取值
#{}:作用更丰富。比如,某个字段为null,此时再mysql数据库中没任何问题,但是oracle不可以。因
为mybatis对所有的null都映射的是原生的JDBC的OTHER类型。Oracle不能正确处理。
此时可以由两种处理方案:
1)#{deptdesc,jdbcType=OTHER}
<insert id="insert" databaseId="orace">
<selectKey keyProperty="deptid" order="AFTER" resultType="Integer">
select dept_seq.currval from dual
</selectKey>
insert into dept values(dept_seq.nextval,#{deptname},#{deptdesc,
jdbcType=OTHER })
</insert>
5> Select****元素
1)查询结果List
<!--public List<Dept> getDeptsByName (String deptame);-->
<select id="getDeptsByName" resultType="com.bd.www.Dept">
select * from dept where deptname like #{deptname}
</select>
2)查询结果为map
i .单条记录的封装(此时的key是查询结果的列名,value是该列对应的值)
<!--public Map<String, Object> getDeptByIdReturnMap(Integer id);-->
<select id="getDeptByIdReturnMap" resultType="map">
select * from dept where deptid = #{deptid}
</select>
ii. 多条记录的封装:如果希望查询的一条记录当成一个对象,希望主键当成该记录的值,可以如下操作
<!--@MapKey("deptid")
public Map<String, Dept> getDepts(String deptname);-->
<select id="getDepts" resultType=" com.bd.www.Dept ">
select * from dept where deptname like #{deptname}
</select>
6>ResultMap****元素
\1) 定义查询的结果与具体的对象之间的对应关系
<resultMap type="com.bd.www.Dept" id="DeptMap">
<!--指定主键列的封装规则 id定义主键会底层有优化;column:指定哪一列
property:指定对应的javaBean属性-->
<id column="id" property="deptid"/>
<!-- 定义普通列封装规则 -->
<result column="name " property="deptname"/>
<!-- 其他不指定的列会自动封装:我们只要写resultMap就把全部的映射规则都写上。 -
->
</resultMap>
<!-- resultMap:自定义结果集映射规则; -->
<!-- public Dept getDeptById(Integer id); -->
<select id="getEmpById" resultMap="DeptMap">
select * from dept where id=#{id}
</select>
\2) 关联对象查询
查询员工信息的同时,希望获取该员工所对应的部门信息。此时pojo 的设计会在员工类中定义一个部门
类的属性。查询的时候,会使用关联查询员工与部门的信息。此时的映射可以如下定义:
<resultMap type="com.bd.www.Emp" id="EmpMap">
<id column="id" property="empid"/>
<result column="empname" property="empname"/>
<result column="deptid" property="dept.deptid"/>
<result column="deptname" property="dept.deptname"/>
</resultMap>
<select id="getEmpAndDept" resultMap="EmpMap">
SELECT e.*, d.id deptid,d.name deptname FROM emp e,tbl_dept d
WHERE e.deptid=d.id AND e.id=#{id}
</select>
7> Association****元素
以上应用使用进行如下配置,也是可以的
<resultMap type="com.bd.www.Emp" id="EmpMap">
<id column="id" property="empid"/>
<result column="empname" property="empname"/>
<!-- association可以指定联合的javaBean对象
property="dept":指定哪个属性是联合的对象
javaType:指定这个属性对象的类型[不能省略]
-->
<association property="dept" javaType="com.bd.www.bean.Department">
<id column="deptid" property="id"/>
<result column="deptname" property="deptname"/>
</association>
</resultMap>
<select id="getEmpAndDept" resultMap="EmpMap">
SELECT e.*, d.id deptid,d.name deptname FROM emp e,tbl_dept d
WHERE e.deptid=d.id AND e.id=#{id}
</select>
可以加入两项配置,使以上应用变为延迟加载
-显示的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
8> Collection
现在希望在查询部门的同时查找到该部门下的员工
<!--嵌套结果集的方式,使用collection标签定义关联的集合类型的属性封装规则 -->
<resultMap type="com.bd.www.Department" id="MyDept">
<id column="id" property="deptid"/>
<result column="deptname" property="deptname"/>
<!-- collection定义关联集合类型的属性的封装规则
ofType:指定集合里面元素的类型-->
<collection property="list" ofType="com.bd.www.Employee">
<!-- 定义这个集合中元素的封装规则 -->
<id column="id" property="empid"/>
<result column="name" property="empname"/>
<result column="email" property="email"/>
</collection>
</resultMap>
<!-- public Dept getDeptByIdPlus(Integer id); -->
<select id="getDeptByIdPlus" resultMap="MyDept">
SELECT d.*,e.id empid,e.name,e.email,
FROM dept d
LEFT JOIN emp e
ON d.id=e.deptid
WHERE d.id=#{id}
</select>
扩展:以上resultMap还可以如下配置
<!-- 扩展:多列的值传递过去: 将多列的值封装map传递;
column="{key1=column1,key2=column2}"
fetchType="lazy":表示使用延迟加载; - lazy:延迟 - eager:立即
-->
<resultMap type="com.bd.www.Department" id="MyDept">
<id column="id" property="deptid"/>
<result column="deptname" property="deptname"/> <collection
property="list" select="com.bd.www.EmpDao.selectEmpsByDeptid"
column="{deptId=id}" fetchType="lazy"></collection>
</resultMap>
<!--{deptId=id}:是因为deptid:是在empDao中配置文件取参数的名字。Id:是列名-->
<resultMap type="com.bd.www.Employee" id="MyEmpDis">
<id column="id" property="empid"/>
<result column=" name" property="empame"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!--column:指定判定的列名 javaType:列值对应的java类型 -->
<discriminator javaType="string" column="gender">
<!--女生 resultType:指定封装的结果类型;不能缺少。/resultMap-->
<case value="0" resultType="com.bd.www.Employee">
<association property="dept"
select="com.bd.www.dao.DeptDao.getDeptById"column="deptid">
</association>
</case>
<!--男生 ;如果是男生,把name这一列的值赋值给email; -->
<case value="1" resultType="com.bd.www.Employee">
<id column="id" property="empid"/>
<result column=" name" property="empame"/>
<result column="name" property="email"/>
<result column="gender" property="gender"/>
</case>
</discriminator>
</resultMap>
<!--如果性别为男,查询部门信息,如果性别为女-->
<resultMap id="baseMap" type="com.it.bean.Emp">
<id column="empid" property="empid"></id>
<result column="empname" property="empname"></result>
<result column="empsalary" property="empsalary"></result>
<result column="empsex" property="empsex"></result>
<result column="deptid" property="deptid"></result>
<result column="jobid" property="jobid"></result>
</resultMap>
<resultMap id="empMap3" type="com.it.bean.Emp" extends="baseMap">
<discriminator javaType="string" column="empsex">
<case value="男" resultType="com.it.bean.Emp">
<association property="dept" column="{id=deptid}"
select="com.it.dao.DeptDao.findDeptByid"></association>
</case>
<case value="女" resultType="com.it.bean.Emp">
<association property="job" column="{id=jobid}"
select="com.it.dao.JobDao.findById"></association>
</case>
</discriminator>
</resultMap>
三、动态SQL
1、if
<!-- if:判断 -->
<!-- 查询员工,要求,携带了哪个字段查询条件就带上这个字段的值
使用where标签后,自己会自动删除第一个的 and 或者or条件 (第一个的and或者or)
-->
<!-- public List<Employee> getEmpsByConditionIf(Employee employee); -->
<select id="getEmpsByConditionIf" resultType="com.bd.www.Employee">
select * from emp
<where>
<!-- test:判断表达式 c:if test
从参数中取值进行判断遇见特殊符号应该去写转义字符: &&:&& 或者 and
‘’ 或者使用转义字符 "" -->
<if test="id!=null">
and id=#{id}
</if>
<if test="name!=null && name!=""">
and name like #{name}
</if>
<if test="email!=null and email.trim()!=’’;">
and email=#{email}
</if>
<!-- ognl会进行字符串与数字的转换判断 "0"==0 -->
<if test="gender==0 or gender==1">
and gender=#{gender}
</if>
</where>
</select>
2、choose
<!-- public List<Employee> getEmpsByConditionChoose(Employee employee); -->
<select id="getEmpsByConditionChoose" resultType="com.bd.www.Employee">
select * from emp
<where>
<!-- 如果带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个 -->
<choose>
<when test="id!=null">
id=#{id}
</when>
<when test="name!=null">
name like #{name}
</when>
<otherwise>
gender = 0
</otherwise>
</choose>
</where>
</select>
3、trim
<!--public List<Employee> getEmpsByConditionTrim(Employee employee); -->
<select id="getEmpsByConditionTrim" resultType="com.bd.www.Employee">
select * from tbl_employee
<!-- 后面多出的and或者or where标签不能解决
prefix="":前缀:trim标签体中是整个字符串拼串 后的结果。
prefix给拼串后的整个字符串加一个前缀
prefixOverrides="":前缀覆盖: 去掉整个字符串前面多余的字符
suffix="":后缀 suffix给拼串后的整个字符串加一个后缀
suffixOverrides=""后缀覆盖:去掉整个字符串后面多余的字符-->
<trim prefix="where" suffixOverrides="and">
<if test="id!=null">
id=#{id} and
</if>
<if test="lastName!=null && lastName!=""">
last_name like #{lastName} and
</if>
<if test="email!=null and email.trim()!=""">
email=#{email} and
</if>
<!-- ognl会进行字符串与数字的转换判断 "0"==0 -->
<if test="gender==0 or gender==1">
gender=#{gender}
</if>
</trim>
</select>
4、set
<!--public void updateEmp(Employee employee); -->
<update id="updateEmp">
<!-- Set标签的使用 -->
update tbl_employee
<set>
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email},
</if>
</set>
where id=#{id}
<!-- Trim:更新拼串
update tbl_employee
<trim prefix="set" suffixOverrides=",">
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email},
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</trim>
where id=#{id} -->
</update>
5、foreach
<!--collection:指定要遍历的集合:
list类型的参数会特殊处理封装在map中,map的key就叫list
item:将当前遍历出的元素赋值给指定的变量
separator:每个元素之间的分隔符
open:遍历出所有结果拼接一个开始的字符
close:遍历出所有结果拼接一个结束的字符
index:索引。遍历list的时候是index就是索引,item就是当前值
遍历map的时候index表示的就是map的key,item就是map的值
#{变量名}就能取出变量的值也就是当前遍历出的元素
-->
<!--public List<Employee> getEmpsByConditionForeach(List<Integer> ids); -->
<select id="getEmpsByConditionForeach"
resultType="com.bd.www.Employee">
select * from emp
<foreach collection="list" item="item_id" separator=","
open="where id in(" close=")">
#{item_id}
</foreach>
</select>