目录
一.新增
二.修改
三.删除、根据Id查询
1.删除用户
2.根据Id查询
四.模糊查询
1.使用#定义参数
2.使用$定义参数
五.分页查询
1.顺序传参
2.@Param传参
3.POJO传参
4.Map传参
六.聚合查询
七.主键回填
一.新增
通过Navicat查看数据库,右键user表,点击设计表,可以看到表的各种设置
可以看到user表的id列是自增的,也就是插入数据时不需要设置id字段!
在接口文件里定义插入接口函数
然后在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">
<!-- namespace设置映射的文件-->
<mapper namespace="com.first.mapper.UserMapper">
<!--
id是方法名;
resultType表示结果的对象,即列表的泛型;
查找用select标签 -->
<select id="findAll" resultType="com.first.pojo.User">
select * from user
</select>
<!-- 在values中传入对象的属性时就把属性放到#{}中-->
<insert id="add" parameterType="com.first.pojo.User">
insert into user (username,sex,address) values (#{username},#{sex},#{address})
</insert>
</mapper>
不必在SqlMapConfig.xml中再配置了,因为之前已经配置过映射文件。
在User.java中再添加一个三个参数的构造函数,因为插入时不用插入id字段
public User(String username, String sex, String address) {
this.username = username;
this.sex = sex;
this.address = address;
}
编写测试方法 ,在 测试类里添加如下方法:
@Test
public void testAdd() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session = factory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User user=new User("塔姆","男","西安");
userMapper.add(user);
//提交事务,增删改都需要提交事务,否则数据库不会改变
session.commit();
//释放资源
session.close();
is.close();
}
注意:
- 当接口方法的参数类型为POJO类型时,SQL语句中绑定参数时使用 #{POJO的属性名} 即可。
- MyBatis事务默认手动提交,所以在执行完增删改方法后,需要手动调用SqlSession对象的事务提交方法,否则数据库将不发生改变。
运行测试方法,也要看到数据库里插入成功!
二.修改
优化测试类
我们发现 MyBatis 的测试方法在操作数据库前都需要获取代理对象,操作数据库后都需要释放资源,可以利用 Junit 的 前置后置方法 ,优化测试类代码。
这样Junit就会自动执行获取代理对象和释放资源的方法。 新建一个TestUserMapper2.java测试类,利用前置和后置将公共部分取出,降低代码冗余度。
package com.first.mapper;
import com.first.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 TestUserMapper2 {
//全局变量
InputStream is=null;
SqlSession session =null;
UserMapper userMapper =null;
//前置方法:测试类里的测试方法执行之前都会先执行前置方法
@Before
public void before() throws IOException {
//(1)读取核心配置文件
is= Resources.getResourceAsStream("SqlMapConfig.xml");
//(2)创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//(3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
SqlSessionFactory factory = builder.build(is);
//(4)SqlSessionFactory对象获取SqlSession对象
session = factory.openSession();
//(5)SqlSession对象获取代理对象
userMapper = session.getMapper(UserMapper.class);
}
后置方法:测试类里的测试方法执行之后都会执行后置方法
@After
public void after() throws IOException {
//(7)释放资源
session.close();
is.close();
}
}
在UserMapper.java中添加接口函数:
//修改数据
void update(User user);
在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">
<!-- namespace设置映射的文件-->
<mapper namespace="com.first.mapper.UserMapper">
<!--
id是方法名;
resultType表示结果的对象,即列表的泛型;
查找用select标签 -->
<select id="findAll" resultType="com.first.pojo.User">
select *
from user
</select>
<!-- 在values中传入对象的属性时就把属性放到#{}中-->
<!-- 参数类型是User对象-->
<insert id="add" parameterType="com.first.pojo.User">
insert into user (username, sex, address)
values (#{username}, #{sex}, #{address})
</insert>
<update id="update" parameterType="com.first.pojo.User">
update user
set username=#{username},
sex=#{sex},
address=#{address}
where id = #{id}
</update>
</mapper>
编写测试函数:
package com.first.mapper;
import com.first.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 TestUserMapper2 {
//全局变量
InputStream is=null;
SqlSession session =null;
UserMapper userMapper =null;
//前置方法:测试类里的测试方法执行之前都会先执行前置方法
@Before
public void before() throws IOException {
//(1)读取核心配置文件
is= Resources.getResourceAsStream("SqlMapConfig.xml");
//(2)创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//(3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
SqlSessionFactory factory = builder.build(is);
//(4)SqlSessionFactory对象获取SqlSession对象
session = factory.openSession();
//(5)SqlSession对象获取代理对象
userMapper = session.getMapper(UserMapper.class);
}
后置方法:测试类里的测试方法执行之后都会执行后置方法
@After
public void after() throws IOException {
//(7)释放资源
session.close();
is.close();
}
@Test
public void testUpdate(){
User user = new User(7,"吉米","女","深圳");
userMapper.update(user);
session.commit();
}
}
运行:
看到数据库已经更新
三.删除、根据Id查询
1.删除用户
在UserMapper.java中添加接口函数:
//删除用户
void delete(int userId);
在UserMapper.xml中配置映射:
<delete id="delete" parameterType="int">
delete
from user
where id = #{sbID}
</delete>
注:当方法的参数类型是简单数据类型时, #{} 中可以写任意名称 ,就像形参那样,叫啥都行,比如我这里叫了sbID
简单数据类型:基本数据类型、字符串等
编写测试方法 :
@Test
public void testDelete(){
userMapper.delete(7);
session.commit();
}
运行发现果然删除成功!
2.根据Id查询
在UserMapper.java中添加接口函数:
//根据id查找用户
User findById(int userId);
在UserMapper.xml中配置映射:
参数是用户的id,是int类型(故而参数名随便起了个userId),返回的结果是一个用户对象,类型是User
<select id="findById" parameterType="int" resultType="com.first.pojo.User">
select * from user where id=#{userId}
</select>
编写测试方法 :
@Test
public void testFindById(){
//查询操作没有修改数据库,不用提交事务
User user = userMapper.findById(1);
System.out.println(user);
}
输出:
User{id=1, username='小热', sex='男', address='北京'}
四.模糊查询
1.使用#定义参数
在UserMapper.java中添加接口函数(即持久层接口添加方法):
//根据用户名进行模糊查询
List<User> findByUsernameLike(String username);
映射文件添加标签 )
<!-- 注意parameterType是小写的string,另一种写法是java.lang.String-->
<select id="findByUsernameLike" parameterType="string" resultType="com.first.pojo.User">
select * from user where username like #{username}
</select>
编写测试方法 :
@Test
public void testFindByUsernameLike(){
List<User> users=userMapper.findByUsernameLike("%小%");
users.forEach(System.out::println);
}
运行:
名字里带小的都能查询出来
User{id=1, username='小热', sex='男', address='北京'}
User{id=2, username='小巴', sex='男', address='上海'}
User{id=3, username='小赵', sex='女', address='广州'}
User{id=4, username='小迪', sex='男', address='北京'}
User{id=5, username='小丽', sex='男', address='太原'}
我们看到在映射文件中, parameterType 的值为 string 而没有写 java.lang.String ,这是为什么呢?
参数 / 返回值类型为基本数据类型 / 包装类 /String 等类型时,我们可以写全类名,也可以写别名。
2.使用$定义参数
模糊查询如果不想在调用方法时参数加 % ,可以使用拼接参数的方式设置 Sql :
<select id="findByUsernameLike" parameterType="string" resultType="com.first.pojo.User">
select * from user where username like '%${value}%'
</select>
测试方法写法如下:
@Test
public void testFindByUsernameLike(){
List<User> users=userMapper.findByUsernameLike("小");
users.forEach(System.out::println);
}
运行测试方法,发现运行成功,名字里带“小”的用户信息都被查出!
#和$ 的区别:
- 1 #表示sql模板的占位符,$表示将字符串拼接到sql模板中。
- 2 #可以防止sql注入(相当于学JDBC时的PreparedStatement),一般能用#就不用$(相当于学JDBC时的Statement)。
- 3 ${}内部的参数名必须写value。
使用 <bind> 定义参数
如果使用 # 还不想在调用方法的参数中添加 % ,可以使用 <bind> , <bind> 允许我们在 Sql 语句以外创建一个变量,并可以将其绑定到当 前的 Sql 语句中。用法如下:
<select id="findByUsernameLike" parameterType="string" resultType="com.first.pojo.User">
<bind name="likeName" value="'%'+username+'%'"/>
select * from user where username like #{likeName}
</select>
测试方法写法如下:
@Test
public void testFindByUsernameLike(){
List<User> users=userMapper.findByUsernameLike("小");
users.forEach(System.out::println);
}
运行结果和上面一致!
五.分页查询
分页查询时, Sql 语句使用 limit 关键字,需要传入开始索引和每页条数两个参数。 MyBatis 的多参数处理有以下方式:
1.顺序传参
Sql 中的参数使用 arg0 , arg1... 或 param1 , param2... 表示参数的顺序。 此方法可读性较低,在开发中不建议使用。
持久层接口方法 :
//下面的注释先把接口函数写完,然后在函数上一行打出/**然后回车就出来了
/**
*
* @param startIndex 开始索引
* @param pageSize 每页条数
* @return
*/
List<User> findPage(int startIndex,int pageSize);
映射文件 :
<!--多个参数时就没必要设置parameterType,因为参数类型可能不同-->
<!-- 其中arg0,arg1也可以换成param1和param2-->
<select id="findPage" resultType="com.first.pojo.User">
select * from user limit #{arg0},#{arg1}
</select>
测试类 :
@Test
public void testFindPage(){
List<User> users = userMapper.findPage(0, 3);
users.forEach(System.out::println);
}
运行测试方法:
User{id=1, username='小热', sex='男', address='北京'}
User{id=2, username='小巴', sex='男', address='上海'}
User{id=3, username='小赵', sex='女', address='广州'}
2.@Param传参
在接口方法的参数列表中通过 @Param 定义参数名称,在 Sql 语句中通过注解中所定义的参数名称指定参数位置。 此方式参数比较直观的,推荐使用。
1
持久层接口方法 :
/**
*
* @param startIndex 开始索引
* @param pageSize 每页条数
* @return
*/
//推荐注解名和参数名一致,这样可读性高
List<User> findPage1(@Param("startIndex") int startIndex, @Param("pageSize") int pageSize);
映射文件 :
<!-- 直接传入参数对应的注解名-->
<select id="findPage1" resultType="com.first.pojo.User">
select * from user limit #{startIndex},#{pageSize}
</select>
测试类 :
@Test
public void testFindPage1(){
List<User> users = userMapper.findPage1(3, 3);
users.forEach(System.out::println);
}
运行测试方法:
User{id=4, username='小迪', sex='男', address='北京'}
User{id=5, username='小丽', sex='男', address='太原'}
User{id=6, username='本本', sex='男', address='西安'}
3.POJO传参
自定义 POJO 类,该类的属性就是要传递的参数,在 SQL 语句中绑定参数时使用 POJO 的属性名作为参数名即可。 此方式推荐使用。
1
自定义 POJO :
在src/main/java/com/first/pojo/下创建PageQuery类
package com.first.pojo;
public class PageQuery {
private int startIndex;
private int pageSize;
public PageQuery() {
}
public PageQuery(int startIndex, int pageSize) {
this.startIndex = startIndex;
this.pageSize = pageSize;
}
public int getStartIndex() {
return startIndex;
}
public void setStartIndex(int startIndex) {
this.startIndex = startIndex;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
}
持久层接口方法 :
/**
* 分页查询
*/
//参数是一个类对象
List<User> findPage2(PageQuery pageQuery);
映射文件:
<!-- pojo传参-->
<select id="findPage2" resultType="com.first.pojo.User" parameterType="com.first.pojo.PageQuery">
select * from user limit #{startIndex},#{pageSize}
</select>
测试函数:
@Test
public void testFindPage2(){
PageQuery pageQuery=new PageQuery(3,3);
List<User> users = userMapper.findPage2(pageQuery);
users.forEach(System.out::println);
}
运行:
User{id=4, username='小迪', sex='男', address='北京'}
User{id=5, username='小丽', sex='男', address='太原'}
User{id=6, username='本本', sex='男', address='西安'}
4.Map传参
如果不想自定义 POJO ,可以使用 Map 作为传递参数的载体,在 SQL语句中绑定参数时使用 Map 的 Key 作为参数名即可。 此方法推荐使用。
1
持久层接口方法:
//参数是一个Map对象
List<User> findPage3(Map<String,Object> params);
映射文件 :
<select id="findPage3" resultType="com.first.pojo.User" parameterType="map">
select * from user limit #{startIndex},#{pageSize}
</select>
测试函数:
@Test
public void testFindPage3(){
Map<String,Object> params = new HashMap();
params.put("startIndex",0);
params.put("pageSize",4);
List<User> users = userMapper.findPage3(params);
users.forEach(System.out::println);
}
运行:
User{id=1, username='小热', sex='男', address='北京'}
User{id=2, username='小巴', sex='男', address='上海'}
User{id=3, username='小赵', sex='女', address='广州'}
User{id=4, username='小迪', sex='男', address='北京'}
在MyBatis中使用Map传参,Map键的类型为: String
六.聚合查询
举例:查询用户总数
持久层接口方法:
int findCount();
映射文件 :
<select id="findCount" resultType="int">
select count(id) from user
</select>
测试方法:
@Test
public void testFindCount(){
System.out.println(userMapper.findCount());
}
运行输出:
7
七.主键回填
有时我们需要获取新插入数据的主键值。如果数据库中主键是自增的,这时我们就需要使用 MyBatis 的主键回填功能。
1
持久层接口方法:
void add2(User user);
映射文件:
<insert id="add2" parameterType="com.first.pojo.User">
<!-- keyProperty:主键属性名,keyColumn:主键列名,resultType:主键类型,
order:执行时机,AFTER是代表在插入之后执行 -->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID();
</selectKey>
insert into user(username,sex,address)
values(#{username},#{sex},#{address})
</insert>
SELECT LAST_INSERT_ID():查询刚刚插入的记录的主键值,只适用于自增主键,且必须和insert语句一起执行。
测试方法:
主键回填即自动递增的逐渐不止体现在数据库中,咱们new的user对象起初并没有赋id值,所以主键回填就是把自动生成的主键回填到user对象的id值上!
@Test
public void testAdd2(){
User user = new User("丽颖", "女", "南京");
//如果换成最开始写的add()函数,那么返回值是0
userMapper.add2(user);
session.commit();
System.out.println(user.getId());
}
运行:
9
到此为止的项目结构如下: