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. 143. Reorder List(List)

    Given a singly linked list L: L0→L1→…→Ln-1→Ln, reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… You must do th ...

  2. Standard shader 和 Standard (Specular setup) Shader

    http://blog.csdn.net/jk823394954/article/details/48594341

  3. pkg-config的妙用

    1.每个lib下都会有个pkg-config文件夹,里面有相应pc文件 修改里面内容可以改变pkg-config显示 2.将.pc文件所在路径添加到PKG_CONFIG_PATH中如: export ...

  4. Zedboard学习(二):zedboard的Linux下交叉编译环境搭建 标签: 交叉编译linuxzedboard 2017-07-04 23:49 19人阅读

    环境准备 首先肯定是要下载xilinx-2011.09-50-arm-xilinx-linux-gnueabi.bin文件,这是官方提供的linux下交叉编译链安装文件,下载地址为:https://p ...

  5. CSS3 圆环状进度条

    HTML: <div class="wrap"> <div class="progress-radial progress-25"> & ...

  6. sql语句表连接

    "Persons" 表: Id_P LastName FirstName Address City 1 Adams John Oxford Street London 2 Bush ...

  7. jQuary总结9:html()的常见用法

    1html() 不传参数 用于获取内容 //html <div> <p></p> <span></span> 文本 </div> ...

  8. CodeForces 682A Alyona and Numbers (水题,数学)

    题意:给定两个数 m,n,问你在从1到 n,和从 1到 m中任选两个数加起来是5的倍数,问你有多少个. 析:先计算 m 和 n中有多少个取模5是从0到4的,然后根据排列组合,相乘就得到了小于等于 m ...

  9. oracle问题:新建了一个PDM文件,建表后生成的sql语句中含有clustered

    问题描述 为了在oracle中新增表,在PDM中建表,使用其生成的sql语句,但是建表不能成功,提示 ORA-00906: 缺失左括号 原因是多了clustered 关键字 情景重现 1. 新建一个p ...

  10. Cannot change version of project facet Dynamic Web Module to 2.4.

    解决办法,找到项目目录,将.settings  target  .classpath  .project 删除 重新引入项目 原因是 web.xml 文件里<web-app 里的与.settin ...