一、MyBatis实现CRUD操作
使用要求:
1、持久层接口和持久层接口的映射配置的包结构必须相同
2、持久层映射配置中 mapper 标签的 namespace 属性取值必须是持久层接口的全限定类名
3、SQL 语句的配置标签<select>
,<insert>
,<delete>
,<update>
的 id 属性必须和持久层接口的
方法名相同。
准备数据库:
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(32) NOT NULL COMMENT '用户名称',
`birthday` DATETIME DEFAULT NULL COMMENT '生日',
`sex` CHAR(1) DEFAULT NULL COMMENT '性别',
`address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user`(`id`,`username`,`birthday`,`sex`,`address`) VALUES (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小王','2018-03-02 15:09:37','女','安徽'),(43,'小二','2018-03-04 11:34:34','女','上海'),(45,'小明','2018-03-04 12:04:06','男','广东'),(46,'杰克','2018-03-07 17:37:26','男','北京'),(48,'小马','2018-03-08 11:44:00','女','北京');
准备数据库对应的实体类:
import java.util.Date;
public class User implements Serializable{
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
(一)查询所有
持久层接口 IUserDao:
import com.fox.pojo.User;
import java.util.List;
//用户的持久层接口
public interface IUserDao {
//查询所有
List<User> findAll();
}
映射配置文件 IUserDao.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">
<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">
<!--配置查询所有-->
<!--id:接口的方法名字,resultType:返回结果的类型,即结果要封装到哪个实体类中-->
<select id="findAll" resultType="com.fox.pojo.User">
select * from user;
</select>
</mapper>
测试类:
import com.fox.dao.IUserDao;
import com.fox.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MybatisTest {
private InputStream in = null;
private SqlSession sqlSession =null;
private IUserDao userDao = null;
@Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.使用 SqlSessionFactory 生产 SqlSession 对象
sqlSession = factory.openSession();
//4.使用 SqlSession 创建 dao 接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
public void destroy() throws IOException {
//释放资源
sqlSession.close();
in.close();
}
@Test
public void testFindAll(){
//使用代理对象执行查询所有方法
List<User> users = userDao.findAll();
for(User user : users) {
System.out.println(user);
}
}
}
(二)根据id查询
持久层接口 IUserDao:
import com.fox.pojo.User;
//用户的持久层接口
public interface IUserDao {
//根据id查询用户
User findById(Integer userId);//参数名随便取
}
映射配置文件 IUserDao.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">
<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">
<!--id:接口的方法名字,resultType:返回结果的类型,即结果要封装到哪个实体类中-->
<!--parameterType:用于指定传入参数的类型。-->
<select id="findById" resultType="com.fox.pojo.User" parameterType="Integer">
<!-- #{}它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的数据。
具体的数据是由#{}里面的内容决定的-->
select * from user where id = #{uid};<!--参数只有一个并且是简单数据类型时,大括号里的参数名可以随意写-->
</select>
</mapper>
测试类:
import com.fox.dao.IUserDao;
import com.fox.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class MybatisTest {
private InputStream in = null;
private SqlSession sqlSession =null;
private IUserDao userDao = null;
@Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.使用 SqlSessionFactory 生产 SqlSession 对象
sqlSession = factory.openSession();
//4.使用 SqlSession 创建 dao 接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
public void destroy() throws IOException {
//释放资源
sqlSession.close();
in.close();
}
@Test
public void testFindById(){
User user = userDao.findById(46);
System.out.println(user);
}
}
(三)查询使用聚合函数(分组函数)
持久层接口 IUserDao:
//用户的持久层接口
public interface IUserDao {
//查询数据表中的总记录数
int findTotal();
}
映射配置文件 IUserDao.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">
<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">
<!--id:接口的方法名字,resultType:返回结果的类型,即结果要封装到哪个实体类中-->
<select id="findTotal" resultType="int">
select count(*) from user;
</select>
</mapper>
测试类:
import com.fox.dao.IUserDao;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class MybatisTest {
private InputStream in = null;
private SqlSession sqlSession =null;
private IUserDao userDao = null;
@Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.使用 SqlSessionFactory 生产 SqlSession 对象
sqlSession = factory.openSession();
//4.使用 SqlSession 创建 dao 接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
public void destroy() throws IOException {
//释放资源
sqlSession.close();
in.close();
}
@Test
public void testFindTotal(){
int total = userDao.findTotal();
System.out.println("共有"+total+"条记录");
}
}
(四)模糊查询
1.方式一
持久层接口 IUserDao:
import com.fox.pojo.User;
import java.util.List;
//用户的持久层接口
public interface IUserDao {
//根据名字查询用户
List<User> findByName(String username);
}
映射配置文件 IUserDao.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">
<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">
<!--id:接口的方法名字,resultType:返回结果的类型,即结果要封装到哪个实体类中-->
<!--parameterType:用于指定传入参数的类型。-->
<select id="findByName" resultType="com.fox.pojo.User" parameterType="String">
select * from user where username like #{username};
</select>
</mapper>
测试类:
import com.fox.dao.IUserDao;
import com.fox.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MybatisTest {
private InputStream in = null;
private SqlSession sqlSession =null;
private IUserDao userDao = null;
@Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.使用 SqlSessionFactory 生产 SqlSession 对象
sqlSession = factory.openSession();
//4.使用 SqlSession 创建 dao 接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
public void destroy() throws IOException {
//释放资源
sqlSession.close();
in.close();
}
@Test
public void testFindByName(){
List<User> userList = userDao.findByName("%王%");
for (User user : userList) {
System.out.println(user);
}
}
}
在控制台输出的执行 SQL 语句如下:
我们在配置文件中没有加入%
来作为模糊查询的条件,所以在测试类中传入字符串实参时,就需要给定模糊查询的标识%
。配置文件中的#{username}
也只是一个占位符,所以 SQL 语句显示为“?
”。
2.方式二
持久层接口 IUserDao:
import com.fox.pojo.User;
import java.util.List;
//用户的持久层接口
public interface IUserDao {
//根据名字查询用户
List<User> findByName(String username);
}
映射配置文件 IUserDao.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">
<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">
<!--id:接口的方法名字,resultType:返回结果的类型,即结果要封装到哪个实体类中-->
<!--parameterType:用于指定传入参数的类型。-->
<select id="findByName" resultType="com.fox.pojo.User" parameterType="String">
select * from user where username like '%${value}%';
<!--我们在上面将原来的#{}占位符,改成了${value}。注意如果用模糊查询的这种写法,
那么${value}的写法就是固定的,不能写成其它名字。-->
</select>
</mapper>
测试类:
import com.fox.dao.IUserDao;
import com.fox.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MybatisTest {
private InputStream in = null;
private SqlSession sqlSession =null;
private IUserDao userDao = null;
@Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.使用 SqlSessionFactory 生产 SqlSession 对象
sqlSession = factory.openSession();
//4.使用 SqlSession 创建 dao 接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
public void destroy() throws IOException {
//释放资源
sqlSession.close();
in.close();
}
@Test
public void testFindByName(){
List<User> userList = userDao.findByName("王");
for (User user : userList) {
System.out.println(user);
}
}
}
在控制台输出的执行 SQL 语句如下:
可以发现,我们在测试代码中就不需要加入模糊查询的匹配符%
了,这两种方式的实现效果是一样的,但执行的语句是不一样的。
我们一起来看 TextSqlNode 类的源码:
这就说明了源码中指定了读取的 key 的名字就是”value”,所以我们在绑定参数时就只能叫 value 的名字了
3.两种方式的区别
#{}
与${}
的区别#{}
表示一个占位符号。
通过#{}
可以实现 PreparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,#{}
可以有效防止 sql 注入。#{}
可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单数据类型值,#{}
括号中可以是 value 或其它名称。${}
表示拼接 sql 串。
通过${}
可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换,${}
可以接收简单类型值或 pojo 属性值,如果 parameterType 传输单个简单数据类型值,${}
括号中只能是 value。
- 方式一选用的是PreparedStatement,可以防止sql注入,推荐;而方式二选用的是Statement,不推荐。
(五)增加操作
持久层接口 IUserDao:
import com.fox.pojo.User;
//用户的持久层接口
public interface IUserDao {
//增加用户
void addUser(User user);
}
映射配置文件 IUserDao.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">
<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">
<!--id:接口的方法名字,parameterType:用于指定传入参数的类型。-->
<insert id="addUser" parameterType="com.fox.pojo.User">
<!--由于id是自增的,所以没有id-->
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
</mapper>
由于我们增加用户的方法的参数是一个 User 对象,所以#{}
中要写 User 对象中的属性名称。它用的是 OGNL表达式。
OGNL表达式:
-
它是 apache 提供的一种表达式语言,全称:Object Graphic Navigation Language,对象图导航语言
-
它是通过对象的取值方法来获取数据。在写法上把get给省略了。
比如:我们获取用户的名称
在类中的写法:user.getUsername();
OGNL表达式写法:user.username
-
mybatis中为什么能直接写
#{username}
,而不用#{user.username}
呢:
因为在parameterType中已经提供了属性所属的类,所以此时不需要写对象名。
#{user.username}
它会先去找 user 对象,然后在 user 对象中找到 username 属性,并调用getUsername()
方法把值取出来。但是我们在 parameterType 属性上指定了实体类名称,所以可以省略user.
而直接写 username。
测试类:
import com.fox.dao.IUserDao;
import com.fox.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
public class MybatisTest {
private InputStream in = null;
private SqlSession sqlSession =null;
private IUserDao userDao = null;
@Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.使用 SqlSessionFactory 生产 SqlSession 对象
sqlSession = factory.openSession();
//4.使用 SqlSession 创建 dao 接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
public void destroy() throws IOException {
//增删改,记得提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
in.close();
}
@Test
public void testAddUser(){
User user = new User();
//由于id是自增的,所以没有id
user.setUsername("小白");
user.setBirthday(new Date());
user.setAddress("上海");
user.setSex("女");
userDao.addUser(user);
}
}
注意事项:增删改操作,执行完后记得sqlSession.commit()
提交事务,否则会回滚 。
问题扩展:返回新增用户 id 的值
假如我按以上方法新增用户了,但是我想知道新增的用户的id值怎么办呢?
因为 id 是由数据库的自动增长来实现的,所以就相当于我们要在新增后将自动增长 auto_increment 的值返回。
映射配置文件 IUserDao.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">
<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">
<!--id:接口的方法名字,parameterType:用于指定传入参数的类型。-->
<insert id="addUser" parameterType="com.fox.pojo.User">
<!--keyProperty:实体类中id的名字,keyColumn:数据库中id的名字 resultType:返回值类型 order:此查询在增加操作之前还是之后-->
<selectKey keyProperty="id" keyColumn="id" resultType="Integer" order="AFTER">
select last_insert_id();
</selectKey>
<!--由于id是自增的,所以没有id-->
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
</mapper>
测试类:
import com.fox.dao.IUserDao;
import com.fox.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
public class MybatisTest {
private InputStream in = null;
private SqlSession sqlSession =null;
private IUserDao userDao = null;
@Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.使用 SqlSessionFactory 生产 SqlSession 对象
sqlSession = factory.openSession();
//4.使用 SqlSession 创建 dao 接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
public void destroy() throws IOException {
//增删改,记得提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
in.close();
}
@Test
public void testAddUser(){
User user = new User();
//由于id是自增的,所以没有id
user.setUsername("小白");
user.setBirthday(new Date());
user.setAddress("上海");
user.setSex("女");
System.out.println("保存前:"+user);
userDao.addUser(user);
System.out.println("保存后:"+user);
}
}
(六)更新操作
持久层接口 IUserDao:
import com.fox.pojo.User;
//用户的持久层接口
public interface IUserDao {
//更新用户
void updateUser(User user);
}
映射配置文件 IUserDao.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">
<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">
<!--id:接口的方法名字,parameterType:用于指定传入参数的类型。-->
<update id="updateUser" parameterType="com.fox.pojo.User">
update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id} <!--OGNL表达式-->
</update>
</mapper>
测试类:
import com.fox.dao.IUserDao;
import com.fox.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class MybatisTest {
private InputStream in = null;
private SqlSession sqlSession =null;
private IUserDao userDao = null;
@Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.使用 SqlSessionFactory 生产 SqlSession 对象
sqlSession = factory.openSession();
//4.使用 SqlSession 创建 dao 接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
public void destroy() throws IOException {
//增删改,记得提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
in.close();
}
@Test
public void testUpdateUser(){
User user = new User();
user.setId(46);
user.setUsername("小杰");
user.setBirthday(new Date());
user.setAddress("天津");
user.setSex("男");
userDao.updateUser(user);
}
}
(七)删除操作
持久层接口 IUserDao:
//用户的持久层接口
public interface IUserDao {
//删除用户
void deleteUser(Integer id); //参数名随便取
}
映射配置文件 IUserDao.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">
<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">
<!--id:接口的方法名字,parameterType:用于指定传入参数的类型。-->
<delete id="deleteUser" parameterType="Integer">
delete from user where id = #{uid}; <!--参数只有一个并且是简单数据类型时,大括号里的参数名可以随意写-->
</delete>
</mapper>
测试类:
import com.fox.dao.IUserDao;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class MybatisTest {
private InputStream in = null;
private SqlSession sqlSession =null;
private IUserDao userDao = null;
@Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.使用 SqlSessionFactory 生产 SqlSession 对象
sqlSession = factory.openSession();
//4.使用 SqlSession 创建 dao 接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
public void destroy() throws IOException {
//增删改,记得提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
in.close();
}
@Test
public void testDeleteUser(){
userDao.deleteUser(49);
}
}
MyBatis中手写实现类也可以实现以上的所有CRUD操作,只不过是调用sqlsession中的insert()、update()、delete()、selectList()、selectOne()方法罢了,但是实际开发中不建议手写实现类,代码冗余。
二、MyBatis 与 JDBC 编程的比较
- JDBC中sql 语句写在代码中造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变 java 代码。
MyBatis将 Sql 语句配置在 XXXXDao.xml 或 XXXXMapper.xml 文件中与 java 代码分离。 - JDBC向 sql 语句传参数麻烦,因为 sql 语句的 where 条件不一定,可能多也可能少,占位符需要和参数对应。
MyBatis 自动将 java 对象映射至 sql 语句,通过 statement 中的 parameterType 定义输入参数的类型。 - JDBC对结果集解析麻烦,sql 变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo 对象解析比较方便。
MyBatis 自动将 sql 执行结果映射至 java 对象,通过 statement 中的 resultType 定义输出结果的类型。
三、MyBatis 的参数类型 parameterType 深入
我们在上面中已经介绍了 SQL 语句传参,使用标签的 parameterType 属性来设定。该属性的取值可以是基本类型,引用类型(例如:String 类型),还可以是实体类类型(POJO 类)。同时也可以使用实体类的包装类,本章节将介绍如何使用实体类的包装类作为参数传递。
(一)注意事项
- 基本类型和 String 我们可以直接写类型名称,也可以使用
包名.类名
的方式,例如:parameterType="java.lang.String"
。 - 实体类类型,目前我们只能使用全限定类名。
究其原因,是 mybatis 在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名,而我们的是实体类并没有注册别名,所以必须写全限定类名。在下文中将讲解如何注册实体类
的别名。 - 支持的默认别名,在 mybatis 的官方文档的说明(第 19 页)
我们也可以从源码角度来看它们分别都是如何定义出来的,可以参考 TypeAliasRegistery.class 的源码:
(二)传递 pojo 包装对象
开发中通过 pojo 传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。广泛应用于由多个对象组成查询条件来实现数据的查询。
pojo 包装对象:pojo 类中包含 pojo。
需求:根据用户名查询用户信息,查询条件放到 QueryVo 的 user 属性中。
编写实体类 QueryVo(pojo 包装类):
public class QueryVo{
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
持久层接口 IUserDao:
import com.fox.pojo.QueryVo;
import com.fox.pojo.User;
import java.util.List;
//用户的持久层接口
public interface IUserDao {
//根据QueryVo中的条件查询用户
List<User> findByVo(QueryVo vo);
}
映射配置文件 IUserDao.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">
<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">
<!--id:接口的方法名字,resultType:返回结果的类型,即结果要封装到哪个实体类中-->
<!--parameterType:用于指定传入参数的类型。-->
<select id="findByVo" parameterType="com.fox.pojo.QueryVo" resultType="com.fox.pojo.User">
select * from user where username like #{user.username};
</select>
</mapper>
有人可能会问,前面说的OGNL表达式不是不需要user.
吗?但是这里的parameterType是QueryVo,这个类没有username属性,它的属性是user,因此我们就可以通过user.username
方式获得名字属性。
测试类:
import com.fox.dao.IUserDao;
import com.fox.pojo.QueryVo;
import com.fox.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MybatisTest {
private InputStream in = null;
private SqlSession sqlSession =null;
private IUserDao userDao = null;
@Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.使用 SqlSessionFactory 生产 SqlSession 对象
sqlSession = factory.openSession();
//4.使用 SqlSession 创建 dao 接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
public void destroy() throws IOException {
//增删改,记得提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
in.close();
}
@Test
public void testFindByVo(){
QueryVo vo = new QueryVo();
User user = new User();
user.setUsername("%王%");
vo.setUser(user);
List<User> userList = userDao.findByVo(vo);
for (User user1 : userList) {
System.out.println(user1);
}
}
}
四、MyBatis 的结果类型 resultType 深入
- resultType 属性可以指定结果集的类型,它支持基本类型和实体类类型。
- 需要注意的是,它和 parameterType 一样,如果注册过类型别名的,可以直接使用别名(基本类型和String我们可以直接写类型名称)。没有注册过的必须使用全限定类名。例如:我们的实体类此时必须是全限定类名(下文会讲解如何配置实体类的别名)
- 同时,当是实体类时,还有一个要求,实体类中的属性名称必须和数据表中的字段名保持一致,否则无法实现封装。
那么如果实体类中的属性名称和数据表中的字段名不一致该怎么办呢?
(一)解决属性和字段名不一致问题
字段名: 数据库的列名
属性名: 实体类的成员变量名
例如:对于地址这一栏,数据库的字段名为username和address,而实体类User的属性名改为userName和userAddress
持久层接口 IUserDao:
import com.fox.pojo.User;
import java.util.List;
//用户的持久层接口
public interface IUserDao {
//查询所有
List<User> findAll();
}
映射配置文件 IUserDao.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">
<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">
<!--配置查询所有-->
<!--id:接口的方法名字,resultType:返回结果的类型,即结果要封装到哪个实体类中-->
<select id="findAll" resultType="com.fox.pojo.User">
select * from user;
</select>
</mapper>
测试类:
import com.fox.dao.IUserDao;
import com.fox.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MybatisTest {
private InputStream in = null;
private SqlSession sqlSession =null;
private IUserDao userDao = null;
@Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.使用 SqlSessionFactory 生产 SqlSession 对象
sqlSession = factory.openSession();
//4.使用 SqlSession 创建 dao 接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
public void destroy() throws IOException {
//释放资源
sqlSession.close();
in.close();
}
@Test
public void testFindAll(){
//使用代理对象执行查询所有方法
List<User> users = userDao.findAll();
for(User user : users) {
System.out.println(user);
}
}
}
从结果我们发现名称还是有值的,而地址栏没有值了。
为什么名称会有值呢?
因为:mysql 在 windows 系统中字段名不区分大小写!在Linux系统中才严格区分大小写。
而地址这一栏的值都变为了null,那么如何解决呢?
方案一:
因为MyBatis会根据数据库的字段名去找对应的实体类的属性名,它会将所有字段名转换为小写,然后去找实体类中对应的set方法 ,set方法后面的字段名也转换为小写,与对应数据库的小写形式字段名进行比较;如果不一样就会返回null,因此我们可以只更改set方法名字【不推荐使用】:
把User类中的setUserName()
、setUserAddress()
改为setUsername()
、setAddress()
方案二:
给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">
<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">
<!--配置查询所有-->
<!--id:接口的方法名字,resultType:返回结果的类型,即结果要封装到哪个实体类中-->
<select id="findAll" resultType="com.fox.pojo.User">
select id,username as userName,birthday,sex,address as userAddress from user;
</select>
</mapper>
如果我们的查询很多,都使用别名的话写起来岂不是很麻烦,有没有别的解决办法呢?
方案三:
结果集映射ResultMap【最推荐的方式】
- resultMap 标签可以在查询的字段名和实体类的属性名称不一致时建立对应关系。从而实现封装。
- 在 select 标签中使用 resultMap 属性指定引用即可,就不需要再写resultType了。同时 resultMap 可以实现将查询结果映射为复杂类型的 pojo,比如在查询结果映射对象中包括 pojo 和 list 实现一对一查询和一对多查询。
<?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对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">
<!-- 配置 查询结果的列名和实体类的属性名的对应关系 -->
<!-- id属性:给此结果集映射起个别名 type属性:使用此结果集映射的实体类-->
<resultMap id="userMap" type="com.fox.pojo.User">
<!-- 主键字段的对应 id 标签:用于指定主键字段-->
<id column="id" property="id"/>
<!--非主键字段的对应 result 标签:用于指定非主键字段-->
<!--column 属性:用于指定数据库列名-->
<!--property 属性:用于指定实体类属性名称-->
<result column="username" property="userName"/>
<result column="sex" property="sex"/>
<result column="address" property="userAddress"/>
<result column="birthday" property="birthday"/>
</resultMap>
<!--配置查询所有-->
<!--id:接口的方法名字,resultMap:使用哪个结果集映射-->
<select id="findAll" resultMap="userMap">
select * from user;
</select>
</mapper>
使用方案二的执行效率比方案三高,因为方案三还要加载resultMap执行时间增加,但是当字段多的时候,方案三的开发效率比方案二高。
五、MyBatis 核心配置文件的配置
(一)MyBatis 核心配置文件中配置的内容和顺序
<?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 属性
settings 设置
typeAliases 类型别名
typeHandlers 类型处理器
objectFactory 对象工厂
plugins 插件
environments 环境集合属性对象
environment 环境子属性对象
transactionManager 事务管理
dataSource 数据源
databaseIdProvider 数据库厂商标识
mappers 映射器
- configuration :配置
configuration的元素有:
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?,
objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
注意:在编写配置文件的时候,标签出现的先后顺序必须严格依照以上顺序。
-
properties:引入配置文件 属性是可外部配置且可动态替换的
-
settings:MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
-
typeAliases:为Java类型设置一个短的名字。只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
-
typeHandlers:类处理器
-
objectFactory:对象工厂,MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。
-
plugins:插件
-
environments: 配置环境,这里可以有多套环境 default代表默认的是哪一套,尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
- environment 该元素体中包含了事务管理和连接池的配置 id为环境名称
- transactionManager 决定事务作用域和控制方式的事务管理器
- dataSource 获取数据库连接实例的数据源
- environment 该元素体中包含了事务管理和连接池的配置 id为环境名称
-
databaseIdProvider:数据库厂商标识
-
mappers:映射器,用于告诉 MyBatis 到哪里去找到 SQL 映射语句。
(二)properties标签
在 classpath 下定义 jdbc.properties 文件:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
1.properties标签的resource属性
下面的数据库配置就可以用${key}来获取数据库连接的配置。
<?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">
<!-- mybatis的主配置文件 -->
<configuration>
<!--resource 属性:用于指定 properties 配置文件的位置,要求配置文件必须在类路径下-->
<properties resource="jdbc.properties">
</properties>
<!-- 配置环境 -->
<environments default="mysql">
<!-- 配置mysql的环境-->
<environment id="mysql">
<!-- 配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<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>
<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
<mappers>
<mapper resource="com/fox/dao/IUserDao.xml"/><!--基于xml方式-->
</mappers>
</configuration>
2.properties标签的url属性
我们可以在项目路径下将properties文件拖到浏览器地址栏中,再复制粘贴出来就是该properties文件的url。
<?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">
<!-- mybatis的主配置文件 -->
<configuration>
<!--url属性:是要求按照url的写法来写地址
URL:Uniform Resource Locator 统一资源定位符。它是可以唯一标识一个资源的位置。
它的写法:
http://localhost:8080/mybatisserver/demo1Servlet
协议 主机 端口 URI
URI:Uniform Resource Identifier 统一资源标识符。它是可以在 web 应用中唯一定位一个资源的路径
-->
<properties url="file:///D:/IDEAProjects/MybatisStudy/src/main/resources/jdbc.properties">
</properties>
<!-- 配置环境 -->
<environments default="mysql">
<!-- 配置mysql的环境-->
<environment id="mysql">
<!-- 配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="${jdbc.driver}"/>
<!--xml中不允许&符号直接出现,我们需要使用 & 代替-->
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
<mappers>
<mapper resource="com/fox/dao/IUserDao.xml"/><!--基于xml方式-->
</mappers>
</configuration>
同样地,mapper标签的url属性也是一样的用法。但是url属性不够灵活,resource属性用得多。
(三) typeAliases标签
在前面我们讲了 MyBatis 支持的基本类型和引用类型的默认别名,我们也可以给我们的实体类采用自定义别名方式来开发。
1.自定义别名方式一
<!--使用typeAliases配置别名 -->
<typeAliases>
<!--typeAlias用于配置别名。type属性指定的是实体类全限定类名。alias属性指定别名-->
<typeAlias type="com.fox.pojo.User" alias="user"></typeAlias>
</typeAliases>
当指定了别名就不再区分大小写,一样可以正确使用:
2.自定义别名方式二
<!--使用typeAliases配置别名 -->
<typeAliases>
<!-- 用于指定要配置别名的包,当指定之后,该包下的所有实体类都会注册别名,并且类名就是别名,不再区分大小写-->
<package name="com.fox.pojo"></package>
</typeAliases>
(四)mappers标签
mappers标签告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用, 或统一资源定位符(带有file:/// 的 url),或全限定类名和包名等。四种方法如下:
1.<mapper resource=" " />
使用相对于类路径的资源:
<mappers>
<mapper resource="com/fox/dao/IUserDao.xml"/><!--基于xml方式-->
</mappers>
2.<mapper url=" " />
使用统一资源定位符
<mappers>
<mapper url="file:///D:/IDEAProjects/MybatisStudy/src/main/resources/com/fox/dao/IUserDao.xml" />
</mappers>
3.<mapper class=" " />
使用持久层接口的完全限定类名
<mappers>
<mapper class="com.fox.dao.IUserDao"/> <!--基于注解-->
</mappers>
4.<package name=""/>
将包内的映射文件全部注册为映射器
<mappers>
<package name="com.fox.dao"/>
</mappers>
六、分页的实现
(一)MySQL中的分页
语法 : SELECT * FROM table LIMIT startIndex,pageSize;
好处 : (用户体验好、网络传输快、查询压力小)
推导:
第一页 : LIMIT 0,5
(显示前五条信息,起始位置为0,等同于LIMIT 5)
第二页 : LIMIT 5,5
(显示第二页的五条信息,起始位置为5)
第三页 : LIMIT 10,5
(显示第三页的五条信息,起始位置为10)
…
第N页 : LIMIT (pageNo-1)*pageSize,pageSize
其中pageNo
:页码,pageSize
:单页面显示条数
如何计算出当前起始位置:startIndex = (pageNo-1)* pageSize
例子:
从Student表中查询前五个学生的姓名和学号
SELECT id,username FROM user LIMIT 0,5
数据库中可以实现将查询的结果分页显示,那么在MyBatis中如何实现呢?
(二)MyBatis中的分页
1.方法一:使用Limit实现分页
(1)编写dao接口
import com.fox.pojo.User;
import java.util.List;
import java.util.Map;
public interface IUserDao {
//查询全部用户实现分页
List<User> selectUserByLimit(Map<String,Integer> map);
}
(2)编写对应的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">
<!--mapper标签的namespace对应Mapper接口的类,包名+类名-->
<mapper namespace="com.fox.dao.IUserDao">
<select id="findUserByLimit" parameterType="Map" resultType="com.fox.pojo.User">
select * from user limit #{startIndex},#{pageSize}
</select>
</mapper>
(3)编写测试类
import com.fox.dao.IUserDao;
import com.fox.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class UserTest {
private InputStream in = null;
private SqlSession sqlSession =null;
private IUserDao userDao = null;
@Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.使用 SqlSessionFactory 生产 SqlSession 对象
sqlSession = factory.openSession();
//4.使用 SqlSession 创建 dao 接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
public void destroy() throws IOException {
//提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
in.close();
}
@Test
public void testFindUserByLimit(){
//准备数据(显示第几页的多少条结果)
int pageNo = 1;//当前是第几页
int pageSize = 2; //页面大小
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",(pageNo-1)*pageSize);
map.put("pageSize",pageSize);
List<User> users = userDao.findUserByLimit(map);
for (User user : users) {
System.out.println(user);
}
}
}
显示了第一页的前两条查询结果:
2.方法二:使用RowBounds实现分页
(1)编写dao接口
import com.fox.pojo.User;
import java.util.List;
public interface IUserDao {
//分页查询
List<User> findUserByRowBounds();
}
(2)编写对应的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">
<!--mapper标签的namespace对应Mapper接口的类,包名+类名-->
<mapper namespace="com.fox.dao.IUserDao">
<select id="findUserByRowBounds" resultType="com.fox.pojo.User">
select * from user
</select>
</mapper>
(3)编写测试类
import com.fox.dao.IUserDao;
import com.fox.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class UserTest {
private InputStream in = null;
private SqlSession sqlSession =null;
private IUserDao userDao = null;
@Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.使用 SqlSessionFactory 生产 SqlSession 对象
sqlSession = factory.openSession();
//4.使用 SqlSession 创建 dao 接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
public void destroy() throws IOException {
//提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
in.close();
}
@Test
public void testFindUserByLimit(){
//准备数据(显示第几页的多少条结果)
int pageNo = 1;//当前是第几页
int pageSize = 2; //页面大小
RowBounds rowBounds = new RowBounds((pageNo-1)*pageSize,pageSize);
//注意点:使用RowBounds就不能使用getMapper了,而是selectList\selectMap\selectOne等
//selectList: 接收一个List
//selectMap: 接收一个Map
//selectOne : 接收只有一个对象的时候
//这里是selectList,传入的参数依次是对应的Mapper接口中的抽象方法的全域名、一个指定的Objec对象(一般为null)以及RowBounds对象
List<User> users = sqlSession.selectList("com.fox.dao.IUserDao.findUserByRowBounds", null, rowBounds);
for (User user : users) {
System.out.println(user);
}
}
}
显示了第一页的前两条查询结果:
3.两种方法(Limit和RowBounds)的区别
- Limit 方式是在SQL层面实现分页,需要先通过反射拿到Mapper接口,再通过SqlSession获得Mapper接口的实现类,通过实现类传入参数(起始位置,页面大小),实现分页显示。
- RowBounds 在代码层面实现分页,无需获取Mapper接口的实现类,需要先通过有参构造(传入分页参数)创建RowBounds对象,再通过SqlSession对象调用
selectList
、selectMap
或者selectOne
方法实现分页。传入的参数依次是对应的Mapper接口中的抽象方法的全域名,一个指定的Objec对象(一般为null),以及RowBounds对象。RowBounds 本质其实就是封装了Limit方式。