Mybatis进阶
接口代理方式实现dao层🚩
四大要求:
1、映射配置文件中的mapper标签的namespace要和接口的全类名相同;2、映射配置文件中select标签的id属性值要和接口的方法名相同;3、映射配置文件中select标签的parameterType属性值要和方法的参数类型一致;4、映射配置文件中select标签的resultType属性值为查询出来的结果要封装成的类型,
例如:实现的接口中的方法返回值类型是List<Student>,表示查询出来的结果要封装成Student对象,则resultType属性值应为Student
代码实现步骤:
1、编写dao层接口
/*
dao层接口
*/
public interface StudentMapper {
//查询全部
public abstract List<Student> selectAll();
//根据id查询
//新增数据
//修改数据
//删除数据
//多条件查询
//根据多个id查询
}
2、编写映射配置文件
<!--namespace="com.itheima.mapper.StudentMapper" :接口的全类名-->
<mapper namespace="com.itheima.mapper.StudentMapper">
<!--id必须是接口的方法名-->
<!--查询所有学生信息-->
<select id="selectAll" resultType="student">
select * from student
</select>
<!--根据id查询学生信息-->
<!--添加学生-->
<!--修改学生信息-->
<!--删除学生信息-->
</mapper>
3、编写测试类实现
public class StudentTest {
@Test
public void testSelectAll() throws IOException {
//1、加载核心配置文件
InputStream is = Resources.getResourceAsStream("MybatisConfig.xml");
//2、获取工厂对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//3、获取SqlSession核心对象
SqlSession sqlSession = factory.openSession(true);
//4、获取接口的实现类代理对象,执行操作
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = mapper.selectAll();
//5、处理/打印结果
list.forEach(stu-> System.out.println(stu));
//6、释放资源
sqlSession.close();
}
}
动态SQL
1、使用场景:应用搜索中,用户根据自己想要的结果,按自己想要的特定字段搜索条件进行筛选查询
2、使用if标签进行搜索条件判断
<!--多条件查询-->
<select id="selectCondition" resultType="student" parameterType="student">
select * from student
<where>
<if test="id != null"> 判断条件
id=#{id}
</if>
<if test="name != null and name !='' ">
and name=#{name}
</if>
<if test="age != null">
and age=#{age}
</if>
</where>
</select>
3、foreach标签完成根据多个id的同时查询
配置文件编写:
<!--多个id查询-->
<select id="selectByIds" resultType="student">
select * from student
<where>
<!--
collection="" 遍历数组写array,遍历集合写list,遍历map集合写map
open="id in(" 以xxx开头
close=")" 以xxx结尾
item="" 遍历的每一个元素的名称,变量名随便写,写啥后面就用啥
separator="," 遍历的容器内元素的分隔符
index="" 如果遍历的是数组或者单列集合,就表示索引。如果遍历的是map就表示key
-->
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id} <!--#{id} 大括号中的名称就是item的属性值-->
</foreach>
</where>
</select>
测试类编写:
@Test
public void testSelectByIds() throws IOException {
//1、加载核心配置文件
InputStream is = Resources.getResourceAsStream("MybatisConfig.xml");
//2、获取工厂对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//3、获取SqlSession核心对象
SqlSession sqlSession = factory.openSession(true);
//4、获取接口的实现类代理对象,执行操作
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//创建查询条件
List<Integer> ids = List.of(1, 2,3);
//执行查询
List<Student> list = mapper.selectByIds(ids);
//5、处理/打印结果
list.forEach(student -> System.out.println(student));
//6、释放资源
sqlSession.close();
}
分页插件🚩
使用步骤:
1、导入第三方jar包
jsqlparser-3.1.jar 和pagehelper-5.1.10.jar
2、在mybatis的核心配置文件中集成分页插件
<!--集成PageHelper分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
3、测试类里进行查询数据库操作之前使用分页助手设置查询的页数和每页展示的条数
@Test
public void testSelectAll() throws IOException {
//1、加载核心配置文件
//2、获取工厂对象
//3、获取SqlSession核心对象
//4、获取接口的实现类代理对象,执行操作
//分页插件设置分页查询的条件 -->需要紧邻操作数据库的执行语句
//参数1:当前页码,参数2:每页显示条数
PageHelper.startPage(1,3);
//执行数据库操作语句
List<Student> list = mapper.selectAll();
//封装分页信息
PageInfo<Student> info = new PageInfo<>(students);
//打印分页信息
System.out.println("当前页数:"+info.getPageNum());
System.out.println("每页展示条数:"+info.getPageSize());
System.out.println("总页数:"+info.getPages());
System.out.println("总条数:"+info.getTotal());
System.out.println("当前页条数:"+info.getSize());
System.out.println("上一页:"+info.getPrePage());
System.out.println("下一页:"+info.getNextPage());
System.out.println("是否是第一页:"+info.isIsFirstPage());
System.out.println("是否是最后一页:"+info.isIsLastPage());
//5、处理结果
list.forEach(stu-> System.out.println(stu));
//6、释放资源
sqlSession.close();
}
mybatis进行多表操作🚩
resultMap标签的作用:手动定义查询结果和domain层类对象的映射关系,一般用来处理查询结果和domain层的类属性不一致情况。
一对一关系表
核心:配置映射文件OneToOneMapper.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">
<mapper namespace="com.itheima.mapper.OneToOneMapper">
<!--配置查询结果和domain层对象的对应关系-->
<resultMap id="oneToOne" type="card">
<!--封装主键信息,column="id"列名称,property="id" domain层中类的属性名称-->
<id column="id" property="id"/>
<!--封装非主键信息-->
<result column="number" property="number"/>
<!--配置包含的对象映射关系,一对一关系用association标签,property表示被包含的对象所对应的属性名,要和类中定义的成员属性名保持一致,javaType="person"表示domain层类的属性类型-->
<association property="p" javaType="person">
<!--封装主键信息-->
<id column="pid" property="id"/>
<!--封装非主键信息-->
<result column="name" property="name"/>
<result column="age" property="age"/>
</association>
</resultMap>
<!-- 测试一对一关系:查询所有card的信息,包括每个card对应的person信息-->
<!--注意:封装结果集不再使用resultType属性,而是使用resultMap属性-->
<select id="selectAll" resultMap="oneToOne">
select c.*,p.NAME,p.age from card c ,person p where c.pid=p.id;
</select>
</mapper>
一对多关系表
核心:配置映射文件OneToMany.xml
一对多关系表和多对多关系表查询只需要将一对一关系表配置文件里的resultMap标签下面的子标签association换成collection
<resultMap id="oneToMany" type="Classes">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
<!--将剩下的查询结果映射到students中,映射集合中的对象,ofType=""表示集合中的对象类型-->
<collection property="students" ofType="student">
<id column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="sage" property="age"/>
</collection>
</resultMap>
多对多关系表
核心:配置映射文件ManyToMany.xml,同一对多关系表查询配置文件
注意:javaType属性和ofType属性的区别?
javaType的值用来表示bean对象的属性类型,ofType的值用来表示集合中元素的类型。
mybatis拓展
1、Mapper接口中方法传递多个参数【重要】🚩
问题引入:
之前我们使用的Mapper中的方法都是空参或者一个参数,那如果有多个参数呢?例如:一个登录的方法public User login(String username,String password)使用mybatis又该如何查询?
准备环境:
public interface StudentMapper {
//多个参数查询
public abstract List<Student> findByNameAndAge(String name, Integer age);
}
<?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="com.itheima.mapper.StudentMapper">
<!--多个参数查询-->
<select id="findByNameAndAge" resultType="Student">
select * from student
<where>
name=#{name} and age=#{age}
</where>
</select>
</mapper>
测试类测试:
//多个参数查询
@Test
public void testFindByNameAndAge() throws IOException {
InputStream is = Resources.getResourceAsStream("MybatisConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = studentMapper.findByNameAndAge("张三", 23);
students.forEach(stu -> System.out.println(stu));
}
测试结果:报错
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'name' not found. Available parameters are [arg1, arg0, param1, param2]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'name' not found. Available parameters are [arg1, arg0, param1, param2]
原因分析:
当我们调用mapper.findStudentByNameAndAge("李四",24)方法时,mybatis会执行动态代理中的invoke方法,该方法会判断参数的个数,如果不是1个,就会将多个参数存到Map集合中,需要我们使用@Param注解指定存进去的key。
改进:
public interface StudentMapper {
//多个参数查询
public abstract List<Student> findByNameAndAge(@Param("name") String name,@Param("age") Integer age);
}
总结:
当我们的接口中方法参数有多个时,需要使用@Param("")指定参数在mybatis中的名称,注意:#{name}中的name名称必须要和@Param("name")中的name名称一致.
2、mybatis实现添加一条数据后返回该数据的id
需求引入:
在我们的现实开发中很多表是有关系的,很多业务不止操作一次数据库,可能是多次多张表。例如,我们刚注册了一个person信息,在person表中就有一条person数据并且会自动生成一个id值,那么我又需要获取到这个id值,将该person的id添加到card中。简单来说就是要使用到刚添加person生成的那个id值,该如何实现?
方法一:【推荐】
在添加操作的映射配置文件中按如下配置:
<!--添加学生信息,或者自增的id值-->
<!--
useGeneratedKeys="true":MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键
keyProperty="id":表示将返回的主键值设置到student对象的id属性身上,
调用完inser()方法之后可以使用student.getId()获取生成的id值
-->
<insert id="insert" parameterType="student" useGeneratedKeys="true" keyProperty="id">
insert into student(id,name,age) values(#{id},#{name},#{age})
</insert>
方法二:【了解】
如下配置映射文件:
<insert id="insert" parameterType="student">
<!--order="AFTER"的值必须是after,表示在执行完insert之后执行-->
<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
SELECT last_insert_id()
</selectKey>
insert into student(id,name,age) values(#{id},#{name},#{age})
</insert>




