使用要求:
1、持久层接口和持久层接口的映射配置的包结构必须相同
2、持久层映射配置中 mapper 标签的 namespace 属性取值必须是持久层接口的全限定类名
3、SQL 语句的配置标签,
,
,
的 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:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<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);
}
}
}
持久层接口 IUserDao:
import com.fox.pojo.User;
//用户的持久层接口
public interface IUserDao {
//根据id查询用户
User findById(Integer userId);//参数名随便取
}
映射配置文件 IUserDao.xml:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<select id="findById" resultType="com.fox.pojo.User" parameterType="Integer">
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:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<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+"条记录");
}
}
持久层接口 IUserDao:
import com.fox.pojo.User;
import java.util.List;
//用户的持久层接口
public interface IUserDao {
//根据名字查询用户
List<User> findByName(String username);
}
映射配置文件 IUserDao.xml:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<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 语句显示为“?
”。
持久层接口 IUserDao:
import com.fox.pojo.User;
import java.util.List;
//用户的持久层接口
public interface IUserDao {
//根据名字查询用户
List<User> findByName(String username);
}
映射配置文件 IUserDao.xml:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<select id="findByName" resultType="com.fox.pojo.User" parameterType="String">
select * from user where username like '%${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 的名字了
#{}
与
${}
的区别
#{}
表示一个占位符号。#{}
可以实现 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:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<insert id="addUser" parameterType="com.fox.pojo.User">
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
insert>
mapper>
由于我们增加用户的方法的参数是一个 User 对象,所以#{}
中要写 User 对象中的属性名称。它用的是 OGNL表达式。
user.getUsername();
user.username
#{username}
,而不用#{user.username}
呢:#{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 是由数据库的自动增长来实现的,所以就相当于我们要在新增后将自动增长 auto_increment 的值返回。
映射配置文件 IUserDao.xml:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<insert id="addUser" parameterType="com.fox.pojo.User">
<selectKey keyProperty="id" keyColumn="id" resultType="Integer" order="AFTER">
select last_insert_id();
selectKey>
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:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<update id="updateUser" parameterType="com.fox.pojo.User">
update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}
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:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<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()方法罢了,但是实际开发中不建议手写实现类,代码冗余。
我们在上面中已经介绍了 SQL 语句传参,使用标签的 parameterType 属性来设定。该属性的取值可以是基本类型,引用类型(例如:String 类型),还可以是实体类类型(POJO 类)。同时也可以使用实体类的包装类,本章节将介绍如何使用实体类的包装类作为参数传递。
包名.类名
的方式,例如:parameterType="java.lang.String"
。开发中通过 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:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<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);
}
}
}
那么如果实体类中的属性名称和数据表中的字段名不一致该怎么办呢?
字段名: 数据库的列名
属性名: 实体类的成员变量名
例如:对于地址这一栏,数据库的字段名为username和address,而实体类User的属性名改为userName和userAddress
持久层接口 IUserDao:
import com.fox.pojo.User;
import java.util.List;
//用户的持久层接口
public interface IUserDao {
//查询所有
List<User> findAll();
}
映射配置文件 IUserDao.xml:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<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语句取别名【字段少的时候推荐使用】
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<select id="findAll" resultType="com.fox.pojo.User">
select id,username as userName,birthday,sex,address as userAddress from user;
select>
mapper>
如果我们的查询很多,都使用别名的话写起来岂不是很麻烦,有没有别的解决办法呢?
结果集映射ResultMap【最推荐的方式】
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<resultMap id="userMap" type="com.fox.pojo.User">
<id column="id" property="id"/>
<result column="username" property="userName"/>
<result column="sex" property="sex"/>
<result column="address" property="userAddress"/>
<result column="birthday" property="birthday"/>
resultMap>
<select id="findAll" resultMap="userMap">
select * from user;
select>
mapper>
使用方案二的执行效率比方案三高,因为方案三还要加载resultMap执行时间增加,但是当字段多的时候,方案三的开发效率比方案二高。
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 映射器
<!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为环境名称
databaseIdProvider:数据库厂商标识
mappers:映射器,用于告诉 MyBatis 到哪里去找到 SQL 映射语句。
在 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
下面的数据库配置就可以用${key}来获取数据库连接的配置。
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties">
properties>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC">transactionManager>
<dataSource type="POOLED">
<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>
<mappers>
<mapper resource="com/fox/dao/IUserDao.xml"/>
mappers>
configuration>
我们可以在项目路径下将properties文件拖到浏览器地址栏中,再复制粘贴出来就是该properties文件的url。
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties url="file:///D:/IDEAProjects/MybatisStudy/src/main/resources/jdbc.properties">
properties>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC">transactionManager>
<dataSource type="POOLED">
<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>
<mappers>
<mapper resource="com/fox/dao/IUserDao.xml"/>
mappers>
configuration>
同样地,mapper标签的url属性也是一样的用法。但是url属性不够灵活,resource属性用得多。
在前面我们讲了 MyBatis 支持的基本类型和引用类型的默认别名,我们也可以给我们的实体类采用自定义别名方式来开发。
<typeAliases>
<typeAlias type="com.fox.pojo.User" alias="user">typeAlias>
typeAliases>
<typeAliases>
<package name="com.fox.pojo">package>
typeAliases>
mappers标签告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用, 或统一资源定位符(带有file:/// 的 url),或全限定类名和包名等。四种方法如下:
使用相对于类路径的资源:
<mappers>
<mapper resource="com/fox/dao/IUserDao.xml"/>
mappers>
使用统一资源定位符
<mappers>
<mapper url="file:///D:/IDEAProjects/MybatisStudy/src/main/resources/com/fox/dao/IUserDao.xml" />
mappers>
使用持久层接口的完全限定类名
<mappers>
<mapper class="com.fox.dao.IUserDao"/>
mappers>
将包内的映射文件全部注册为映射器
<mappers>
<package name="com.fox.dao"/>
mappers>
语法 : 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中如何实现呢?
import com.fox.pojo.User;
import java.util.List;
import java.util.Map;
public interface IUserDao {
//查询全部用户实现分页
List<User> selectUserByLimit(Map<String,Integer> map);
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<select id="findUserByLimit" parameterType="Map" resultType="com.fox.pojo.User">
select * from user limit #{startIndex},#{pageSize}
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.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);
}
}
}
import com.fox.pojo.User;
import java.util.List;
public interface IUserDao {
//分页查询
List<User> findUserByRowBounds();
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fox.dao.IUserDao">
<select id="findUserByRowBounds" 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.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);
}
}
}
显示了第一页的前两条查询结果:
selectList
、selectMap
或者selectOne
方法实现分页。传入的参数依次是对应的Mapper接口中的抽象方法的全域名,一个指定的Objec对象(一般为null),以及RowBounds对象。RowBounds 本质其实就是封装了Limit方式。