Hibernate与Mybatis的本质区别和应用场景

  • Hibernate:标准的ORM框架,不需要写SQL语句,但是优化和修改SQL语句比较难。

    • 应用于需求变化固定的中小型的项目,例如后台管理系统、ERP、ORM、OA。

  • Mybatis:专注SQL本身,SQL的优化比较方便,是不完全的ORM。

    • 主要适用于需求变化较多的项目,例如互联网项目。

mybatis开发dao的方法

SqlSession的应用场合

SqlSessionFactoryBuilder

通过SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory
将SqlSessionFactoryBuilder当成一个工具类使用即可,不需要使用单例管理SqlSessionFactoryBuilder。
在需要创建SqlSessionFactory时候,只需要new一次SqlSessionFactoryBuilder即可。

SqlSessionFactory

通过SqlSessionFactory创建SqlSession,使用单例模式管理sqlSessionFactory(工厂一旦创建,使用一个实例)。
mybatis和spring整合后,可以使用Ioc容器管理。

SqlSession

SqlSession是一个面向用户(程序员)的接口。
SqlSession中提供了很多操作数据库的方法:如:selectOne(返回单个对象)、selectList(返回单个或多个对象)。
SqlSession是线程不安全的,在SqlSesion实现类中除了有接口中的方法(操作数据库的方法)还有数据域属性。
SqlSession最佳应用场合在方法体内,定义成局部变量使用。

原始的DAO开发方法

程序员需要编写DAO和DAO的实现类。需要向DAO实现类中注入SqlSessionFactory,在方法体内通过SqlSessionFactory来创建SqlSession。

  • 定义DAO接口

public interface UserDAO {

    /**
* 根据本id查询用户
*/
User findUserById(int id) throws Exception; /**
* 添加用户
*/
void insertUser(User user) throws Exception; /**
* 根据id删除用户
*/
void deleteUser(int id) throws Exception;
}
  • DAO实现类

/**
* @ClassName: UserDAOImpl
* @Description: DAO实现类(注意:SqlSession是非线程安全的,故不能声明为全局的)
*/
public class UserDAOImpl implements UserDAO { SqlSessionFactory sqlSessionFactory;
/**
* 向DAO实现类中注入SqlSessionFactory(此处通过构造方法注入)
*/
public UserDAOImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
} @Override
public User findUserById(int id) throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserById", id);
sqlSession.close();
return user;
} @Override
public void insertUser(User user) throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.insert("test.insertUser", user);
sqlSession.commit();
sqlSession.close();
} @Override
public void deleteUser(int id) throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.delete("test.deleteUser", id);
sqlSession.commit();
sqlSession.close();
} }

新建一个源代码目录命名为test,在UserDAOImpl类中鼠标右键新建一个Junit Test Case,更换源代码目录为test并勾选我们需要测试的方法:

public class UserDAOImplTest {

    private SqlSessionFactory sqlSessionFactory;

    /**
* 此方法在执行测试之前执行,得到一个SqlSessionFactory
*/
@Before
public void setUp() throws Exception { sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("SqlMapConfig.xml"));
} @Test
public void testFindUserById() throws Exception {
// 创建UserDAO(在构造中注入SqlSessionFactory)
UserDAO userDAO = new UserDAOImpl(sqlSessionFactory);
User user = userDAO.findUserById(1);
System.out.println(user);
} @Test
public void testInsertUser() throws Exception {
UserDAO userDAO = new UserDAOImpl(sqlSessionFactory);
User user = new User();
user.setSex("2");
user.setUsername("孙悟空");
user.setAddress("方寸灵台山");
user.setBirthday(new Date());
userDAO.insertUser(user);
} @Test
public void testDeleteUser() throws Exception {
UserDAO userDAO = new UserDAOImpl(sqlSessionFactory);
userDAO.deleteUser(27);
} }

原始DAO开发中存在的问题

  • DAO的接口实现类中存在大量的模板方法,设想:可以将重复的代码提取出来。

  • 在SqlSession的方法时将Statement的id硬编码在你DAO的实现类中。

  • 调用sqlSession的相关方法传入参数是泛型,即使传入错误的参数,编译时候也不会报错。

Mapper接口开发

程序员只需要编写Mapper接口(相当于DAO接口)和Mapper.xml。Mapper接口的编写需要遵守相关规范:

  1. mapper.xml中的命名空间等于Mapper接口类的全路径;

  2. mapper.java中的方法名和mapper.xml中的statement的id一致;

  3. mapper接口中方法的输入参数类型和mapper.xml中的ParameterType一致;

  4. mapper接口中的方法的返回值类型和mapper.xml中的ResultType一致。

遵循以上规范,mybatis就可以自动生成(反射机制)相关的mapper代理的实现类的对象。

UserMapper.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进行分类管理(SQL隔离)
此处采用Mapper代理开发,命名空间为Mapper接口的地址
-->
<mapper namespace="org.gpf.mapper.UserMapper"> <!-- statement的id和Mapper接口中的方法名一致
parameterType和Mapper接口中的方法参数一致
resultType和Mapper接口中的方法返回值一致
-->
<!-- 根据id查询用户 -->
<select id="findUserById" parameterType="int" resultType="org.gpf.po.User">
SELECT * FROM user WHERE id = #{id};
</select> <!-- 根据用户名查找用户 -->
<select id="findUsersByName" parameterType="java.lang.String" resultType="org.gpf.po.User">
<!-- 易出错
SELECT * FROM user WHERE username LIKE #{value};
-->
<!-- 使用${}进行SQL拼接 -->
SELECT * FROM user WHERE username LIKE '%${value}%';
</select>
<!-- 添加用户 -->
<insert id="insertUser" parameterType="org.gpf.po.User">
<selectKey keyProperty="id" order="AFTER" resultType="int">
SELECT LAST_INSERT_ID();
</selectKey>
INSERT INTO user(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address});
</insert>
<!-- 删除用户 -->
<delete id="deleteUser" parameterType="java.lang.Integer">
DELETE FROM user WHERE id = #{id}
</delete>
<!-- 更新用户 -->
<update id="updateUser" parameterType="org.gpf.po.User">
UPDATE user SET username = #{username},birthday=#{birthday},sex=#{sex},address=#{address} WHERE id = #{id}
</update>
</mapper>

UserMapper.java

/**
* @Description: 用户管理相关Mapper接口
*/
public interface UserMapper { /**
* 根据本id查询用户
*/
User findUserById(int id) throws Exception; /**
* 根据用户名模糊查询用户
*/
List<User> findUsersByName(String name) throws Exception; /**
* 添加用户
*/
void insertUser(User user) throws Exception; /**
* 根据id删除用户
*/
void deleteUser(int id) throws Exception;
}

完成前面2步之后不要忘了在映射文件SqlMapConfig.xml中加载UserMapper.xml哦!

<!-- 配置映射文件 -->
<mappers>
<mapper resource="sqlmap/User.xml"/>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>

针对UserMapper接口编写Junit单元测试:

public class UserMapperTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
public void setUp() throws Exception { sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("SqlMapConfig.xml"));
} @Test
public void testFindUserById() throws Exception { // 得到SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 得到UserMapper对象(Mybatis自动生成Mapper代理对象)
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 调用UserMapper的方法
User user = userMapper.findUserById(1);
System.out.println(user);
// 关闭SqlSession
sqlSession.close();
} @Test
public void testFindUsersByName() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.findUsersByName("明");
System.out.println(users);
sqlSession.close();
} @Test
public void testInsertUser() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setAddress("晴川");
user.setUsername("百里屠苏");
user.setBirthday(new Date());
user.setSex("1");
userMapper.insertUser(user);
sqlSession.commit();
sqlSession.close();
System.out.println(user.getId());
} @Test
public void testDeleteUser() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.deleteUser(28);
sqlSession.commit();
sqlSession.close();
} }
  • 我们比较疑问的就是我们在UserMapper.xml的根据用户名查找用户的ResultType使用的是普通的POJO,但是我们自己的Mapper接口中返回值是List类型。这不就造成了类型不一致么?但是,实际上代理对象内部调用了selectOne()或者selectList(),代理对象内部会自动进行判断是否是单独的POJO选用合适的方法。

  • Mapper接口中方法的参数只有一个是否会影响系统的维护?DAO层的代码是被业务层公用的,即使Mapper接口的参数只有一个我们也可以使用包装的POJO来满足系统需求。

  • 注意:持久层中方法的参数中可以使用包装类型,但是Service层中不建议使用包装类型(不利于业务层的拓展维护)。

SqlMapConfig.xml文件

属性配置

该配置文件是mybatis的全局配置文件,配置内容如下:

properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)

将数据库连接参数单独配置在db.properties中,只需要在SqlMapConfig.xml中加载db.properties的属性值。
在SqlMapConfig.xml中就不需要对数据库连接参数硬编码。

将数据库连接参数只配置在db.properties中,原因:方便对参数进行统一管理,其它xml可以引用该db.properties。

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=mysqladmin

在SqlMapConfig中加载属性文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration>
<!-- 加载属性文件 -->
<properties resource="db.properties">
<!-- 一般不建议在这里配置属性
<property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
-->
</properties>
<!-- 与Spring整合后该配置将会被移除 -->
<environments default="development">
<environment id="development">
<!-- 使用JDBC事务进行管理,事务的控制由mybatis管理 -->
<transactionManager type="JDBC">
<property name="" value="" />
</transactionManager>
<!-- 从属性文件中取出数据库的配置信息 -->
<dataSource type="UNPOOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
</configuration>

properties特性:

注意: MyBatis 将按照下面的顺序来加载属性:

  • 在 properties 元素体内定义的属性首先被读取。

  • 然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。

  • 最后读取parameterType传递的属性,它会覆盖已读取的同名属性。

建议:
不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中。在properties文件中定义属性名要有一定的特殊性(防止覆盖),如:XXXXX.XXXXX.XXXX

 settings

settings配置全局参数,mybatis在运行时可以调整一些运行参数。例如:开启二级缓存、开启延迟加载。参数详见:Mybatis settings详解

typeAliases

在mapper.xml中,定义很多的statement,statement需要parameterType指定输入参数的类型、需要resultType指定输出结果的映射类型。

如果在指定类型时输入类型全路径,不方便进行开发,可以针对parameterType或resultType指定的类型定义一些别名,在mapper.xml中通过别名定义,方便开发。

mybatis内置的别名

自定义别名

在SqlMapConfig.xml中配置别名:

<!-- 别名定义 -->
<typeAliases>
<!-- 针对单个别名定义
type:类型的路径
alias:别名
-->
<typeAlias type="org.gpf.po.User" alias="user"/>
</typeAliases>

在mapperr.xml中使用别名替代ResultType:

<!-- 根据id查询用户
此处的resultType指定为SqlMapConfig.xml中配置的别名
-->
<select id="findUserById" parameterType="int" resultType="user">
SELECT * FROM user WHERE id = #{id};
</select>

以上的方法是定义简单的单个别名,我们也可以使用批量别名定义:

<!-- 别名定义 -->
<typeAliases>
<!-- 批量别名定义
指定包名,mybatis自动扫描包中的po类,自动定义别名,别名就是类名(首字母大写或小写都可以)
-->
<package name="org.gpf.po"/>
</typeAliases>

在mapper.xml中我们可以使用以上的别名,此时是大小写不敏感的:

<!-- 根据id查询用户
此处的resultType指定为SqlMapConfig.xml中配置的别名
-->
<select id="findUserById" parameterType="int" resultType="User">
SELECT * FROM user WHERE id = #{id};
</select>

其中批量设置别名比较常用(只需要配置po所在的类路径即可)。

TypeHandlers

类型处理器,mybatis通过typeHandlers完成JDBC类型和java类型的转化。一般来说自带的类型处理器已经够用了,不需要单独定义。

mapper

映射配置。

  • 通过resource一次加载一个映射文件:

  • 通过mapper接口加载单个映射文件,需要遵循相关的规范:将mapper.xml和mapper接口的名称保持一致且在一个目录中(前提是使用mapper代理的方式进行开发)。


  • 批量加载mapper。同样需要遵守上面的规范。

输入映射

通过parameterType来指定输入参数的类型,可以是简单类型、hashmap、pojo。

传递POJO的包装对象

需求:完成用户信息的综合查询,需要传入查询条件很复杂(可能包括用户信息、其它信息,比如商品、订单的)

针对上边需求,建议使用自定义的包装类型的pojo。在包装类型的pojo中将复杂的查询条件包装进去。

  • 新建一个UserCustomer类(该类继承User,实现对Us而的拓展)

/**
* @ClassName: UserCustom
* @Description: 用户拓展类
*/
public class UserCustom extends User { }
  • 新建一个UserQueryVO(在该类内部以组合的方式包装UserCustomer类)

/**
* @ClassName: UserQueryVO
* @Description: 视图层面javabean(用来包装查询条件)
*/
public class UserQueryVO { // 包装所需要的查询条件(可以通过组合的方式包装其他的查询条件,例如:商品、订单)
private UserCustom userCustom; public UserCustom getUserCustom() {
return userCustom;
} public void setUserCustom(UserCustom userCustom) {
this.userCustom = userCustom;
} }
  • mapper.xml。在UserMapper.xml中定义用户信息的综合(复杂的关联查询)查询。

<!-- 用户信息的综合查询
#{UserCustom.sex} 取出POJO包装类型中用户的性别(注意大小写:OGNL表达式)
-->
<select id="findUserList" parameterType="org.gpf.po.UserQueryVO" resultType="org.gpf.po.UserCustom">
SELECT * FROM user WHERE user.sex = #{userCustom.sex} OR user.username LIKE '%${userCustom.username}%'
</select>
  • mapper.java。在UserMapper接口中增加一个方法。

/**
* 用户信息的综合查询
*/
List<UserCustom> findUserList(UserQueryVO userQueryVO) throws Exception;
  • 测试类

@Test
public void testfindUserList() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 创建包装对象,传入查询条件
UserQueryVO userQueryVO = new UserQueryVO();
UserCustom userCustom = new UserCustom();
userCustom.setSex("1");
userCustom.setUsername("明");
userQueryVO.setUserCustom(userCustom);
List<UserCustom> users = userMapper.findUserList(userQueryVO);
System.out.println(users);
}

输出映射

ResultType

使用ResultType进行输出映射只有当查询输出列名和POJO中的字段名一致时,该列才可以映射成功;如果查询出来的列名和属性名全部不一致(例如使用了SQL的别名);一旦有一个查询的输出列名和POJO的属性名一致就会映射成对象。

需求:用户信息的综合查询列表总数,并结合上边的综合查询列表实现分页功能。

mapper.xml

<!-- 用户信息的综合查询总数(分页的基础)
COUNT(*) 和COUNT(1)、COUNT(2)一样
-->
<select id="findUserCount" parameterType="org.gpf.po.UserQueryVO" resultType="int">
SELECT COUNT(1) FROM user WHERE user.sex = #{userCustom.sex} OR user.username LIKE '%${userCustom.username}%'
</select>

mapper.java

/**
* 用户信息的综合查询总数
*/
int findUserCount(UserQueryVO userQueryVO) throws Exception;

测试类:

@Test
public void testFindUserCount() throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 创建包装对象,传入查询条件
UserQueryVO userQueryVO = new UserQueryVO();
UserCustom userCustom = new UserCustom();
userCustom.setSex("1");
userCustom.setUsername("明");
userQueryVO.setUserCustom(userCustom);
int count = userMapper.findUserCount(userQueryVO);
System.out.println(count);
}

小结:

  • 查询出来的结果集只有一行且一列,可以使用简单类型进行输出映射。

  • 输出POJO对象和POJO列表。

    • 不管是输出的pojo单个对象还是一个列表(list中包括pojo),在mapper.xml中resultType指定的类型是一样的。
      在mapper.java指定的方法返回值类型不一样。

生成的动态代理对象中是根据mapper方法的返回值类型确定是调用selectOne(返回单个对象调用)还是selectList (返回集合对象调用 ).

ResultMap

可以使用ResultMap来完成高级的输出结果映射。如果数据库输出的列名和POJO的属性名不一致,就可以通过定义ResultMap对列名和属性名进行我们自己的映射。

范例:使用ResultMap改写以下的SQL,完成输出结果的映射;

SELECT id _id,username _username FROM user WHERE id = #{value}

UserMapper.xml

<!-- 定义ResultMap
将SELECT id _id,username _username FROM user和User类中的属性进行映射
type:ResultType最终映射的java对象的类型(可使用别名)
id:ResultMap的唯一标识
-->
<resultMap type="org.gpf.po.User" id="userResultMap">
<!-- id:查询结果集中的唯一标识,column:查询出的列名,property:POJO的属性名,最终column和property具有映射关系 -->
<id column="_id" property="id" />
<!-- 普通名映射定义 -->
<result column="_username" property="username"/>
</resultMap>
<!-- 使用ResultMap进行输出的映射
resultMap:指定定义的ResultMap的id,如果ResultMap在其他文件中需要指定命名空间
-->
<select id="findUserByIdResultMap" parameterType="int" resultMap="userResultMap">
SELECT id _id,username _username FROM user WHERE id = #{value}
</select>

UserMapper.java

/**
* 根据id查询用户(通过ResultMap手工完成映射)
*/
User findUserByIdResultMap(int id) throws Exception;

测试类:

@Test
public void testFindUserByIdResultMap() throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findUserByIdResultMap(1);
System.out.println(user);
sqlSession.close();
}

小结:

  • 使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。

  • 如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。

动态SQL

mybatis核心对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装。

if判断

需求:用户信息综合查询列表和用户信息查询列表总数这两个statement的定义使用动态sql。对查询条件进行判断,如果输入参数不为空才进行查询条件拼接。

if判断类似于JSTL.

mapper.xml

<!-- 动态SQL -->
<select id="findUserList" parameterType="org.gpf.po.UserQueryVO" resultType="org.gpf.po.UserCustom">
SELECT * FROM user
<!-- where可以自动去掉条件中的第一个AND -->
<where>
<if test="userCustom != null">
<if test="userCustom.sex !=null and userCustom.sex!=''">
AND user.sex = #{userCustom.sex}
</if>
<if test="userCustom.username != null and userCustom.username != ''">
AND user.username LIKE '%${userCustom.username}%'
</if>
</if>
</where>
</select>
<select id="findUserCount" parameterType="org.gpf.po.UserQueryVO" resultType="int">
SELECT COUNT(1) FROM user
<where>
<if test="userCustom != null">
<if test="userCustom.sex != null and userCustom.sex != ''">
AND user.sex = #{userCustom.sex}
</if>
<if test="userCustom.username != null and userCustom.username != ''">
AND user.username LIKE '%${userCustom.username}%'
</if>
</if>
</where>
</select>

maper.java

/**
* 用户信息的综合查询
*/
List<UserCustom> findUserList(UserQueryVO userQueryVO) throws Exception; /**
* 用户信息的综合查询总数
*/
int findUserCount(UserQueryVO userQueryVO) throws Exception;

测试类

@Test
public void testfindUserList() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 取消部分查询条件
UserQueryVO userQueryVO = new UserQueryVO();
UserCustom userCustom = new UserCustom();
// userCustom.setSex("1");
userCustom.setUsername("明");
userQueryVO.setUserCustom(userCustom);
List<UserCustom> users = userMapper.findUserList(userQueryVO);
System.out.println(users);
} @Test
public void testFindUserCount() throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 取消部分查询条件
UserQueryVO userQueryVO = new UserQueryVO();
UserCustom userCustom = new UserCustom();
userCustom.setSex("1");
// userCustom.setUsername("明");
userQueryVO.setUserCustom(userCustom);
int count = userMapper.findUserCount(userQueryVO);
System.out.println(count);
}

sql片段

以上的代码中mapper.xml中的SQL语句有一些重复,我们可以将上面的动态SQL中共有的部分抽取出来形成自己的sql片段,在其他的Statement中就可以使用此SQL片段。

  • 定义并使用SQL片段

<!-- 定义SQL片段
id:SQL片段的唯一标识 经验:SQL片段一般是基于单表的,这样的话SQL片段的可重用性高
在SQL片段中不要包含where
-->
<sql id="query_user_where">
<if test="userCustom != null">
<if test="userCustom.sex != null and userCustom.sex != ''">
AND user.sex = #{userCustom.sex}
</if>
<if test="userCustom.username != null and userCustom.username != ''">
AND user.username LIKE '%${userCustom.username}%'
</if>
</if>
</sql>
<select id="findUserCount" parameterType="org.gpf.po.UserQueryVO" resultType="int">
SELECT COUNT(1) FROM user
<where>
<!-- 使用SQL片段,如果不在本文件中需要加上命名空间 -->
<include refid="query_user_where"></include>
</where>
</select>

for-each

向sql传递数组或List,mybatis使用foreach解析。

例如:在用户查询列表和查询总数的statement中增加多个id输入查询。
两种方法:

SELECT * FROM USER WHERE id=1 OR id=10 OR id=16

SELECT * FROM USER WHERE id IN(1,10,16)
  • 在输入参数类型中添加List<Integer> ids传入多个id(并添加getter和setter)

  • 修改mapper.xml中的SQL片段

<!-- 定义SQL片段
id:SQL片段的唯一标识 经验:SQL片段一般是基于单表的,这样的话SQL片段的可重用性高
在SQL片段中不要包含where
-->
<sql id="query_user_where">
<if test="userCustom != null">
<if test="userCustom.sex != null and userCustom.sex != ''">
AND user.sex = #{userCustom.sex}
</if>
<if test="userCustom.username != null and userCustom.username != ''">
AND user.username LIKE '%${userCustom.username}%'
</if>
</if>
<if test="ids != null">
<!-- 使用for-each遍历传入的ids
collection:指定输入 对象中集合属性
item:每个遍历生成对象中
open:开始遍历时拼接的串
close:结束遍历时拼接的串
separator:遍历的两个对象中需要拼接的串
-->
<!-- 使用实现下边的sql拼接:
AND (id=1 OR id=10 OR id=16)
-->
<foreach collection="ids" item="user_id" open="AND (" close=")" separator="OR">
<!-- 每个遍历需要拼接的串 -->
id=#{user_id}
</foreach>
</if>
</sql>

测试类:

@Test
public void testfindUserList() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class); UserQueryVO userQueryVO = new UserQueryVO();
UserCustom userCustom = new UserCustom();
userCustom.setUsername("明"); // 传入多个id
List<Integer> ids = Arrays.asList(1,16,22,25);
userQueryVO.setId(ids);
userQueryVO.setUserCustom(userCustom);
List<UserCustom> users = userMapper.findUserList(userQueryVO);
System.out.println(users);
}
<!-- 实现  and id IN(1,10,16)拼接 -->
<foreach collection="ids" item="user_id" open="and id IN(" close=")" separator=",">
每个遍历需要拼接的串
#{user_id}
</foreach>

MyBatis框架流程的更多相关文章

  1. 从 0 开始手写一个 Mybatis 框架,三步搞定!

    阅读本文大概需要 3 分钟. MyBatis框架的核心功能其实不难,无非就是动态代理和jdbc的操作,难的是写出来可扩展,高内聚,低耦合的规范的代码. 本文完成的Mybatis功能比较简单,代码还有许 ...

  2. 自己实现一个简化版Mybatis框架

    MyBatis框架的核心功能其实不难,无非就是动态代理和jdbc的操作,难的是写出来可扩展,高内聚,低耦合的规范的代码.本文完成的Mybatis功能比较简单,代码还有许多需要改进的地方,大家可以结合M ...

  3. mybatis源码专题(2)--------一起来看下使用mybatis框架的insert语句的源码执行流程吧

    本文是作者原创,版权归作者所有.若要转载,请注明出处.本文以简单的insert语句为例 1.mybatis的底层是jdbc操作,我们先来回顾一下insert语句的执行流程,如下 执行完后,我们看下数据 ...

  4. MyBatis框架基础详细开发流程

    MyBatis 项目已托管到GitHub,大家可以去GitHub查看下载!并搜索关注微信公众号 码出Offer 领取各种学习资料! 一.框架概述 1.1 什么是框架? 软件的半成品,解决了软件开发过程 ...

  5. SSM(Spring+SpringMVC+MyBatis)框架整合开发流程

    回忆了 Spring.SpringMVC.MyBatis 框架整合,完善一个小demo,包括基本的增删改查功能. 开发环境 IDEA MySQL 5.7 Tomcat 9 Maven 3.2.5 需要 ...

  6. Java Mybatis 框架入门教程

    一.Mybatis介绍 MyBatis是一款一流的支持自定义SQL.存储过程和高级映射的持久化框架.MyBatis几乎消除了所有的JDBC代码,也基本不需要手工去 设置参数和获取检索结果.MyBati ...

  7. MyBatis框架及原理分析

    MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架,其主要就完成2件事情: 封装JDBC操作 利用反射打通Java类与SQL语句之间的相互转换 MyBatis的主要设计目的就 ...

  8. MyBatis框架概述

    MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花费精力去处理例如注册驱动.创建connection.创建statement.手动设 ...

  9. Mybatis框架入门

    Mybaits框架 一.什么是Mybatis MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了googl ...

随机推荐

  1. 【HDU3853】LOOPS

    题意 有一个R*C的方格.一个人想从(1,1)走到(r,c).在每个格子都有三种选择,向下,向右,或者原地不动.每个格子里的每个选择都有一定的概率.而每次移动都需要消耗2点的能量,问期望消耗的能量是多 ...

  2. shell if,case,for,while语法

    #shell if的语法 if [空格 xxx 空格] then echo xxxxx exit 1/2/3/4/.... 0表示正确. elif [空格 xxx 空格] then echo xxxx ...

  3. iOS中NSFileManager文件常用操作整合

    //获取Document路径 + (NSString *)getDocumentPath { NSArray *filePaths = NSSearchPathForDirectoriesInDoma ...

  4. 关于防SQL注入敏感词过滤问题

    关于对字符的过滤问题sql查询条件过滤掉单引号是否就安全了呢? 在文章最后一段管理员做了敏感字符的过滤,管理员过滤掉了空格,而攻击者通过 /**/ 来代替空格绕过了过滤字符.感觉很有成就感,呵呵呵呵. ...

  5. 实践作业4:Web测试实践(小组作业)每日任务记录4

    昨天周日平安夜,给大家都放了假,故昨日博客未更新,今天回复博客更新. (一)今日任务更新 编号 人员 任务更新 1 侯欢 已经完成了对两个网站基本功能的分析,已形成基本功能分析报告. 2 余晨晨 上次 ...

  6. Yii2在Form中处理短信验证码的Validator,耦合度最低的短信验证码验证方式

    短信验证码在目前大多数web应用中都会有,本文介绍一个基于Yii2 Validator方式的验证码验证方式. 在其他文章中看到的方式大多比较难做到一次封装,多次重用. 使用此方式的好处自然不用多说,V ...

  7. How attach Java source(为eclipseIDE附加资源)

    In Eclipse, when you press Ctrl button and click on any  Class names, the IDE will take you to the s ...

  8. Hadoop-2.4.0分布式安装手册

    目录 目录 1 1. 前言 2 2. 部署 2 2.1. 机器列表 2 2.2. 主机名 2 2.2.1. 临时修改主机名 3 2.2.2. 永久修改主机名 3 2.3. 免密码登录范围 4 3. 约 ...

  9. 编写高质量代码改善C#程序的157个建议——建议152:最少,甚至是不要注释

    建议152:最少,甚至是不要注释 以往,我们在代码中不写上几行注释,就会被认为是钟不负责任的态度.现在,这种观点正在改变.试想,如果我们所有的命名全部采用有意义的单词或词组,注释还有多少存在的价值. ...

  10. 编写高质量代码改善C#程序的157个建议——建议143:方法抽象级别应在同一层次

    建议143:方法抽象级别应在同一层次 看下面代码: class SampleClass { public void Init() { //本地初始化代码1 //本地初始化代码2 RemoteInit( ...