13、基于Spring的Mybatis框架学习笔记
Mybatis框架的作用
1、分析原始JDBC的操作缺陷
1.1、原始JDBC代码的缺点
- 代码结构化,模板代码、主要变化是SQL语句的变化
- 每次使用都需要注册驱动,获取连接,如果一大坨人去使用JDBC那就回注册一大坨驱动和获取很多次的连接对象,导致资源用了就造,用完就毁,非常浪费资源
- SQL语句,结构化查询语言;如果我要对SQL语句进行修改,那么这个SQL语句和java代码是不是就耦合死了?
- 实体数据和数据库表的映射需要人为进行配置
1.2如果原始JDBC代码需要进行优化,该怎么做?
2、Mybatis的概述
2.1、图解
2.2、概述
- 基于java的dao层的框架,内部封装了jdbc,开发者只需要关注SQL语句本身,不需要花费精力去配置什么连接池,获取连接对象等繁杂的过程
- 通过xml或者注解的方式,将要被执行的各种个结果集配置起来,通过java对象与结果集中的sql语句动态参数进行映射生成最终执行的sql语句
- mybatis自动将结果集的内容映射到java对象当中
- 采用ORM(Object、Relation、Mapping)思想,对象关系映射,对象与数据表的关系映射,对jdbc进行了封装,屏蔽了底层访问jdbc api的访问细节(数据库连接池,获取数据库操作对象,获取结果集,封装结果集内容),让我们不用与JDBC的api打交道,就可以完成数据库的持久化操作
3、Mybatis的开发步骤
3.1、添加Mybatis的坐标
3.2、数据表和User的实体类(student和User类)
3.3、编写映射文件UserMapper.xml
-
创建UserMapper相对应的配置文件配置好约束头
-
<?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定义一个命名空间,因为这个配置文件需要处理很多的sql语句,而且我执行SQL语句的时候需要知道是在什么地方执行的,那肯定是我这个命名空间下的这个方法
-
也就是userMappler.finAll()
-
<!-- 配置mapper --> <mapper namespace="userMapper"> <!-- 写一个查询语句 --> <select id="findAll"> select * from student </select> </mapper>
-
-
那么查询结束Mybatis是不是需要帮我把结果集封装到实体中,他怎么才能知道实体在哪里呢?所以这个查询语句里面还需要配置一个resultType,结果,类型,也就意味着查询出来的结果集要到那个对象内部去封装?(com.waves.pojo.User)
-
<!-- 配置mapper --> <mapper namespace="userMapper"> <!-- 写一个查询语句 --> <select id="findAll" resultType="com.waves.pojo.User"> select * from student </select> </mapper>
-
3.4、编写核心文件SqlMapConfig.xml
1、configuration,意为配置,也就是现在我们要开始配置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>
</configuration>
2、在内部配置当前数据源的环境--enviroments,说明可以配置很多的数据源环境
-
参数解析
-
default="development"默认情况下使用这个环境(我指定的enviroment)
-
<!-- default-默认情况下使用这个环境 --> <environments default="development"> <environment id="development"> <transactionManager type=""></transactionManager> <dataSource type=""></dataSource> </environment> </environments>
3、transactionManager:事务管理器,那肯定JDBC嘛
-
dataSource数据源:池化思想POOLED
-
内部需要参数(驱动、url地址、username、password)
-
<dataSource type="POOLED"> <!-- 配置属性嘛,多大点事 --> <!-- 驱动 --> <property name="driver" value="com.mysql.jdbc.Driver" /> <!-- url --> <property name="url" value="jdbc:mysql://localhost:3306/week9useSSL=false" /> <!-- root --> <property name="username" value="root" /> <!-- psw --> <property name="password" value="010115" /> </dataSource>
-
-
4、Mybatis在进行数据库操作时,会从我们配置的这个数据源中获得connection对象,使用完成之后,会将这个对象进行一个归还
5、他需要知道我们这个映射SQL语句的配置文件在哪里
<!-- 他需要知道我们SQL的配置文件在哪里,也就是说我们要加载映射文件 -->
<mappers>
<mapper resource="com.waves.mapper"></mapper>
</mappers>
3.5、编写测试类
1、加载核心配置文件
因为核心配置文件不仅有数据源的配置,还有映射文件的配置,加载了映射文件之后才能进行相对的sql操作
// 获取Mybatis的核心配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
2、获得Mybatis的会话工厂对象
操纵session的核心对象
// 获取Mybatis的会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
3、根据会化工厂对象得到sqlSession的会话对象
// 获得SqlSession对象,就是我们的会话对象嘛
SqlSession sqlSession = sqlSessionFactory.openSession();
4、根据这个会话对象去执行sql语句
// 通过这个会话对象去执行sql语句,参数,namespace+方法id
List<User> users = sqlSession.selectList("userMapper.findAll");
现在没有使用代理对象,我们只使用了配置文件中的findAll方法,所以是namespace+方法id
5、打印结果,释放资源
System.out.println(users);
// 释放资源
sqlSession.close();
3.6、注意事项,如果是直接使用配置文件当中的方法,那么mapper中的配置需要变为斜杠的方式,而非点
3.7、Mybatis映射文件的概述
4、Mybatis的增删改操作
4.1、插入操作
<!-- 插入操作 -->
<insert id="insertStu">
insert into student values()
</insert>
1、values中的参数是?号还是什么?
结合业务场景,请求发送过来,从控制层-业务层-dao层,他发送过来的结果是不是一个User对象或者是其他对象,你是不是需要把User对象里面的值,放在这个values当中?那么应该怎么做?
2、parameterType:参数类型(对象的全包名)
com.waves.pojo.User
3、占位符代替问号--#
<!-- 插入操作 -->
<insert id="insertStu" parameterType="com.waves.pojo.User">
insert into student values (#{id},#{name},#{username},#{password},
#{sex},#{college},#{dormitory})
</insert>
4、测试
@Test
// 添加学生
public void inserStu() throws IOException {
// 创建一个User对象模拟一下业务操作
User user = new User();
user.setName("test数据");
user.setUsername("test");
user.setPassword("123123");
user.setSex("man");
user.setCollege("智科");
user.setDormitory("C1");
// 获取Mybatis的核心配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 获取Mybatis的会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得SqlSession对象,就是我们的会话对象嘛
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过这个会话对象去执行sql语句,参数,namespace+方法id
int insert = sqlSession.insert("userMapper.insertStu", user);
System.out.println("受影响的行数为:"+insert);
// 释放资源
sqlSession.close();
}
5、注意事项--提交事务commit
上面是没有提交事务的,所以不会存到数据库里面,Mybatis的事物默认是不提交的
查看结果
这个为什么是16?因为这个SQL已经发送到数据库了,MySQL已经开始自增了,但是最终并没有发送数据过去,但是这个位置已经被占了
6、插入操作需要注意的事项
4.2、修改和删除操作
1、修改操作,照葫芦画瓢
<!-- 修改操作 -->
<update id="updateCollege" parameterType="com.waves.pojo.User">
update student set college = #{college} where id = #{id}
</update>
@Test
public void updateStu() throws IOException {
// 创建一个User对象模拟一下业务操作
User user = new User();
user.setId("14");
user.setCollege("智科");
// 获取Mybatis的核心配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 获取Mybatis的会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得SqlSession对象,就是我们的会话对象嘛
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过这个会话对象去执行sql语句,参数,namespace+方法id
int insert = sqlSession.insert("userMapper.updateCollege", user);
System.out.println("受影响的行数为:"+insert);
// 更新操作需要提交事务
sqlSession.commit();
// 释放资源
sqlSession.close();
}
查看结果
2、删除操作,删除我的测试数据
<!-- 删除操作 -->
<delete id="deleteStu" parameterType="com.waves.pojo.User">
delete from student where id=#{id}
</delete>
@Test
public void deleteStu() throws IOException {
// 创建一个User对象模拟一下业务操作
User user = new User();
user.setId("16");
// 获取Mybatis的核心配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 获取Mybatis的会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得SqlSession对象,就是我们的会话对象嘛
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过这个会话对象去执行sql语句,参数,namespace+方法id
int insert = sqlSession.insert("userMapper.deleteStu", user);
System.out.println("测试对象删除成功:"+insert);
// 更新操作需要提交事务
sqlSession.commit();
// 释放资源
sqlSession.close();
}
3、我操,我好像发现了一个恐怖的事情,这尼玛底层源码,我刚刚删除和更新都用的是insert的方式,也就是说,这玩意儿就是个空壳,他真正执行的是配置文件里面的sql语句
牛逼啊
4.3、Mybatis的知识小结
1、parameterType:可以使用单个参数,多个参数就用对象
4.4、核心配置文件的概述(部分)
5、Mybatis核心配置文件
5.1、environments标签
数据库环境配置的,支持多环境配置(environment)
- 默认情况下制定一个默认的环境,environments-default:,你下边有多个environment的环境,我指定一个配置好的环境的id作为我的默认环境
- transactionManager:事务管理器,我们现在用的Mybatis,所以是JDBC类型的(DatasourceTransactionManager)
- datasource:特奶奶的不就是数据源嘛,POOLED是指定当前的数据源类型是连接池的类型
- 也就是我们的池化思想
- 因为是连接池内部肯定要配置多个属性撒
- driver:驱动信息
- url:数据库的链接地址
- username:数据库用户名
- password:数据库密码
5.2、mappers标签
这个标签就是用来加载配置文件的,我们刚刚使用的就是第一种方式,因为核心配置文件在resources目录下的com.waves.mapper里面
第二种就是加载磁盘上的配置文件,也就是文件上的,需要加个参数file
第三个学了注解就可以接触了
第四个:映射器接口(UserMapper),这个接口里面的所有方法的实现,全部注册为映射器,这个直接扫包,很几把不错
5.3、properties:标签
实际开发当中习惯将数据源的东西单独配置成一个properties的文件,这个标签就是拿来加载properties文件的
5.4、typeAliases标签
起别名的,Spring中的常量标签已经给我们起好了别名了,常用的,切记这个标签必须紧跟在properties标签后面
我们可以使用这个标签对我们User的全限定名进行别名的设计
<!-- 定义别名,可以定义多个别名 -->
<typeAliases>
<!-- 给pojo里面的User类定义一个别名 -->
<!-- alias给谁定义别名 -->
<typeAlias type="com.waves.pojo.User" alias="User"></typeAlias>
</typeAliases>
把我们的查询方法里面的返回数据类型从com.waves.pojo.User改为User
5.5、知识小结
6、Mybatis的相对API
6.1、SqlSessionFactory
就是帮我构建一个Session工厂,主要用的API就是这个build方法
内部需要一个参数,这个参数就是用来去加载我们写的Mybatis配置文件的那个输入流,最终返回的是一个Session工厂对象
1、Resources类是工具类,在org.apache.ibatis.io包中,它可以帮我们从类加载路径下、文件系统或者一个web URL中加载资源
因为我们是一个Maven的工程,所以核心配置文件是放在resources下的,可以直接使用文件名,如果在resources里面设置了其他的包,那么就需要加上包的路径(这里说的是resources里面不能被直接加载的包)
最后说一句,工厂就是用来造会话对象的(SqlSession)
2、会话对象SqlSession获取的两种方式
上面的两种方式可以来获取到session对象
- openSession
- 打开一个会话,但是,这种方式得到的Session对象需要我们对事物进行手动的提交(涉及到增删改的操作的时候)
2、openSession(boolean autoCommit)一个boolean
- 传递了true,那就可以帮我们自动提交,是上面那个API的优化
- 为false,那功能就和第一个一样了
6.2、SqlSession会话对象(最重要的一个对象)
最强大的一个类,这里会看到所有执行语句,提交事务或者回滚事务和获取映射器实例的方法
7、Mybatis的Dao层实现
7.1、传统实现
真正Mybatis实现的时候是在Dao层实现的
传统实现其实就和我们测试类里面写的一样
-
定义一个实现类实现我们的UserMapper接口
-
public class UserMapperImpl implements UserMapper { public List<User> SelectAll() throws IOException { // 获取Mybatis的核心配置文件 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); // 获取Mybatis的会话工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获得SqlSession对象,就是我们的会话对象嘛 SqlSession sqlSession = sqlSessionFactory.openSession(); // 通过这个会话对象去执行sql语句,参数,namespace+方法id List<User> users = sqlSession.selectList("userMapper.findAll"); sqlSession.close(); return users; } }
-
-
开始测试
-
public static void main(String[] args) throws IOException { UserMapper mapper = new UserMapperImpl(); List<User> users = mapper.SelectAll(); System.out.println(users); }
-
7.2、接口代理方式(规范)
采用Mybatis的代理开发,实现DAO层的开发,这种方式是主流(终于来了)
1、接口代理对象的生成的原理
通过会话对象的getMapper方法,内部传递我们那个需要被代理的接口,然后Mybatis框架就会帮我们自动生成这个UserMapper接口的实现类
new UserMapperImpl() = sqlSession.getMapper(Mapper.class)
2、实践
-
创建UserMapper接口,创建一个接口方法查询全部学生
-
与UserMapper.xml响应
-
<!-- 使用接口代理,namespace必须为接口的全限定名 --> <mapper namespace="com.waves.mapper.UserMapper"> <select id="SelectAll" resultType="com.waves.pojo.User"> select * from student </select> </mapper>
-
-
开始测试,获取代理对象,通过代理对象执行方法
-
// 使用代理对象操作Mybatis public void SelectAll() throws IOException { // 获取Mybatis的核心配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); // 获取Mybatis的会话工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获得SqlSession对象,就是我们的会话对象嘛 SqlSession sqlSession = sqlSessionFactory.openSession(); // 使用代理对象 UserMapper mapper = sqlSession.getMapper(UserMapper.class); // 使用代理对象内的方法 List<User> users = mapper.SelectAll(); // 输出打印 System.out.println(users); // 释放资源 sqlSession.close(); }
-
-
8、Mybatis映射文件深入
8.1、动态SQL的查询
业务逻辑复杂时,SQL语句是动态变化的,这个时候原先的简单查询就不管用了
如果出现这种情况该怎么办,因为数据可能为空,他这里的SQL是写死了的
1、if标签
模仿上面的例子,我创建一个多条件查询
-
根据name和username查询用户
-
UserMapper接口
-
// 根据姓名和username查询用户 User selectOne(User user);
-
-
在UserMapper.xml配置文件中对接口的方法进行映射
-
where标签等同于(1=1),if标签,只有第一条就无视and,满足则执行不满足则执行全部
-
<!-- 多条件查询单个 --> <select id="selectOne" parameterType="User" resultType="User"> select * from student <where> <if test="name!=null "> and name=#{name} </if> <if test="username!=null "> and username=#{username} </if> </where> </select>
-
-
查看结果,我准备了一个User对象,我只设置了name的属性
-
成功查询并且没有执行username的后续查询
2、foreach标签
这个比较鸡肋,还是讲一下,这个标签内部的参数分别是什么
- collection:数据类型的意思,他是根据传递进来的数据类型来设置的,我传递的是List集合内部就写list,数组就写array
- open:以什么什么开始,根据查询条件而定比如这个图当中的条件就是where 条件开始 xxxx 条件结束--所以这里是 id in ( 。
- close:以什么结束:那肯定是)结束
- item:这里随便起名字,目的是为了接收List集合当中的变量值
- separator:分隔符的意思,上述查询就是逗号
开始测试
-
语句规划
-
<!-- foreach标签的使用 --> <select id="selectStus" parameterType="list" resultType="User"> select * from student <where> <foreach collection="lisst" open="id in (" close=")" item="id" separator=","> #{id} </foreach> </where> </select>
-
-
测试
-
//foreach标签的使用 @Test public void selectStus() throws IOException { // 测试的List集合 List<Integer> ids = new ArrayList<Integer>(); for(int i = 1; i < 10; i++){ ids.add(i); } // 获取Mybatis的核心配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); // 获取Mybatis的会话工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获得SqlSession对象,就是我们的会话对象嘛 SqlSession sqlSession = sqlSessionFactory.openSession(); // 使用代理对象 UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.selectStus(ids); System.out.println(users); }
-
-
结果
-
3、sql片段抽取
Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的
-
测试用例
-
我直接抽取一个复杂一点的
-
<!-- 片段抽取测试2 --> <sql id="whereNameOrUname"> <where> <if test="name!=null "> and name=#{name} </if> <if test="username!=null "> and username=#{username} </if> </where> </sql>
-
-
替换原有的测试用例
4、动态SQL小结
9、Mybatis核心配置文件的深入
9.1、typeHeadlers标签
1、概述
其实无论是我们在使用配置文件进行SQL查询的时候;还是SQL查询完毕将数据库当中的数据封装到我们的实体类中的时候;Mybatis都对双方进行了一个数据类型的转换,下面就是Mybatis对于常用的一些数据的转换
2、问题的提出
如果我有一些自定义的类型,这些数据类型作为参数传递到Mybatis中(MySQL),他自己默认的一些数据转换,无法满足我的需求,那么这个时候就需要我们使用typeHandlers标签,对我们的类型进行一个自定义的处理
3、实践
3.0、需求分析:
我的实体类中有一个属性为Date的数据类型,存入到数据库的时候理应是时间类型,但是我想让他存入数据库的时候非此类型,而是一个1970年至今的毫秒数,而当我从数据库中取出来的时候,让其显示的是date类型的,现在就需要实现这个功能,这个转换成毫秒数在MySQL中是没有的功能
我在原先的User基础上新增了一个字段birthday,新设置了一个添加的方法
出现了异常
无法转换
3.1、定义转换器类继承自BaseTypeHandler实现类
这个泛型就是你需要转换的java类型,我们现在需要转换的java类型就是Date
3.2、重写四个未实现的方法
其中:setNonNullParameter()方法为java程序设置数据到数据库的回调,getNullbleResult()为查询时MySQL的字符串类型转换为java的Type类型的方法
1、setNonNullParameter(),将java的数据转换成数据库需要的数据,人话
/**
* 时间类型转换为bigint类型
* @param preparedStatement 结果集
* @param i 要转换的字段的位置
* @param date 我们传递进去的时间类型
* @param jdbcType 数据库需要的类型
* @throws SQLException
*/
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
// 得到毫秒数
long time = date.getTime();
// 将其转换为bigint类型,强制转换
preparedStatement.setLong(i,time);
}
2、就是把MySQL里面的那个字段到时候拿过来转换成java类型的方法
/**
* 数据库字段转换为java类型
* @param resultSet 返回的结果集
* @param s 数据库的字段名字
* @return 将其转换的date类型进行一个返回
* @throws SQLException
*/
@Override
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
long aLong = resultSet.getLong(s);
System.out.println("得到的强转类型值为:"+aLong);
// 将毫秒数转换为Date类型
Date date = new Date(aLong);
System.out.println("转换为时间类型值为:"+date);
return date;
}
3.3、在Mybatis核心配置文件中对这个转换器进行注册
3.4、测试
//Date转bigint类型
@Test
public void DateInBigint() throws IOException {
// 创建一个User对象模拟一下业务操作
User user = new User();
user.setName("test数据");
user.setUsername("test");
user.setPassword("123123");
user.setSex("man");
user.setCollege("智科");
user.setDormitory("C1");
user.setBirthday(new Date());
// 获取Mybatis的核心配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 获取Mybatis的会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得SqlSession对象,就是我们的会话对象嘛
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Integer integer = mapper.insertStu(user);
System.out.println(integer);
}
9.2、plugins分页标签
1、导入坐标
2、配置该插件
3、测试
分页相关参数的获取
9.3、知识总结
10、Mybatis多表操作
10.0、表与表之间的关系
三种:
1、一对一
一般是两个表共用一个主键,要不就是某一张表的外键和另一张表的主键相关联
2、一对多,多对一
多的一方,要有一个外键,这个外键要和一的一方的主键相同
3、多对多
有张中间表,来维护两张表的主键
10.1、一对一模型的介绍
我现在这里有两张表,一张用户表,一张订单表,用户可以有多个订单,但是一个订单只能有一个用户,主表为用户表,订单表的外键为uid,内部存储的是主表的用户id
1、创建设计好实体user和order,对内部的参数进行初始化的设置
2、创建好他们两个的Mapper接口类,并在核心配置文件中对其设置别名,其中OrderMapper中设置一个方法,查询该订单所对应的用户信息
3、在OrderMapper的映射文件中规划查询语句
这里的返回类型不能是resultType了,因为,在Order表中我们第四个属性是一个对象的属性,而这个对象注入值的话应该怎么注入?
4、ResultMap的使用
id我们给他取的名字,唯一标识,type还是我们的Order类,但是记得取别名因为这里设置的是别名
5、内部属性的解析
手动指定表中字段与指定实体的一个映射关系
-
id是主键,主键用的封装比较特殊
- column:表中的字段名称
- property:要封装的实体的属性名称
-
-
其他非主键属性一律用result标签
-
那么在Order类中,属性User是一个对象,他的属性应该使用对象.的方式来封装
-
-
<result column="uid" property="user.id"></result> <result column="id" property="user.id"></result> <result column="username" property="user.username"></result> <result column="password" property="user.password"></result> <result column="birthday" property="user.birthday"></result>
-
-
封装完毕后开始规划SQL语句
6、SQL语句的规划
<select id="selectAll" resultMap="orderMap">
select *,od.id oid,u.id uid from orders od,users u
where od.uid=u.id order by oid
</select>
7、测试
@Test
public void selectAll() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 获取Mybatis的会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得SqlSession对象,就是我们的会话对象嘛
SqlSession sqlSession = sqlSessionFactory.openSession();
// 使用代理对象
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
List<Order> orders = orderMapper.selectAll();
System.out.println(orders);
}
完美!
8、association标签--配置User属性的第二种方式(匹配标签)
这个看起来虽然麻烦了点,但是观赏效果更好,且语义明显
内部属性的一个讲解
- peoperty:当前实体类(Order)中的属性的名称(private User user)
- javaType:当前实体类(Order)中,属性的类型(User.class)
<!-- 第二种配置的方式,匹配标签 -->
<association property="user" javaType="user">
<id column="userid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
</association>
10.2、一对多配置的实现
就用之前那个模型,一个订单虽然只能对应一个用户,但是一个用户可以对应多个订单
1、查询一个用户,同时查询该用户具有的订单信息
2、规划接口
3、User实体类新增一个属性,为List集合,泛型为Order类
因为一个用户有多个订单信息
4、Collection标签的使用,内部装的是集合
属性解析
- property:集合的名字
- ofType:集合内部的对象--order
5、返回类型resultMap的设计
<resultMap id="userMap" type="user">
<!-- 设置user的字段信息 -->
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<!-- 第五个属性对象属性的设置 -->
<!-- 集合类型数据的设计 -->
<collection property="orders" ofType="Order">
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="ordermoney" property="ordermoney"></result>
</collection>
</resultMap>
6、致命问题
我上面写的东西是正确的,但是我之前在集合数据里面设计的时候,id那个标签里面
column写的是id,这个时候就出现语义不明确了,也就是说我查询出来的数据,order集合里面的数据id,永远都是1,因为我是根据单个用户查询的,也就会出现接下来这个结果
数据库中的数据
这俩id重复了,默认选择的是第一个id,但是第一个id的值都是1,所以查询出来的数据永远只有单条数据
也就是说我之前写的一对一的案例也写错了,原码我已经修改了
7、测试
// 用户测试
@Test
public void userSelectAll() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 获取Mybatis的会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得SqlSession对象,就是我们的会话对象嘛
SqlSession sqlSession = sqlSessionFactory.openSession();
// 使用代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectOne(1);
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
我真的吐了
10.3、多对多配置的实现
1、需求分析,一张教师表,一张学生表,一张关系表,关系表内部存储着教师表和学生表的主键作为外键
- 教师表
- 学生表
- 关系表
一个老师可以教授多个学生,而学生可以有多个老师
2、SQL语句的规划
这次多表查询就需要根据中间表的信息查询到一个老师教授学生的全部信息了
3、pojo类的编写
-
教师类
- 内部需要封装一个学生的List集合
-
学生类
- 内部也需要封装一个教师的集合
因为只查询教师对应的学生,所以学生的toString方法我没有将教师集合加入进去
4、配置文件的SQL规划和对象注入关系
和一对多一样的
5、测试
@Test
public void selectAll() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 获取Mybatis的会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得SqlSession对象,就是我们的会话对象嘛
SqlSession sqlSession = sqlSessionFactory.openSession();
// 使用代理对象
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
List<Teacher> students = mapper.selectAll();
System.out.println(students);
sqlSession.close();
}
10.4、知识总结
11、Mybatis注解开发
11.1、注解开发的优点
简单,方便,开发速度快,注解开发会成为后期项目开发的一种趋势
11.2、常用的注解
11.3、常用注解的开发,快速实现简单的增删改查
首先在mybatis核心配置文件当中,将映射关系mappers中的参数需要修改一下 ,将包内的映射接口全部注册为映射器--com.waves.mapper
我们接下来将SQL语句使用注解的方式写在接口当中,映射关系还存在吗?当然存在,在哪里?在注解当中,那么核心配置文件当中,加载映射文件就替换为加载我们接口当中的映射关系
1、@Select(查询)(全部查询)
加载好映射关系以后,开始使用我们的@Select注解
规划好SQL语句之后,进行我们的测试
1.1、@Befor注解
这个注解是作用在测试包下的,他的作用是,类似抽取的概念,所有的@Test注解的方法中,在执行之前,优先执行@Befor注解下的内容,所以我们可以将获取SqlSessionFactory工厂的步骤统一抽取出来
// 核心抽取,所有Test执行前先执行这个语句
@Before
public void Loging() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 获取Mybatis的会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得SqlSession对象,就是我们的会话对象嘛
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 使用代理对象
mapper = sqlSession.getMapper(UserMapper.class);
}
1.2、测试
-
使用mapper测试我们的方法
-
测试结果为
2、@select注解(单个查询)--@Param注解
在这里如果我们需要进行条件查询,多条件查询的情况下我们需要使用@Param注解,这个注解的意思是,将变量值注入到这个注解内的值当中,完成一个映射的关系,然后再将这个注解的值,放入我们的SQL语句当中
param注解当中的值,必须和参数站位符的值是一样的,这样才能完成一个映射关系
2.1、测试
-
使用mapper调用我们规划好的方法
-
查看结果
3、@insert注解
顾名思义,插入数据
-
规划SQL语句
- 注意,参数占位符的值必须和实体的属性一致才能完成映射
-
测试数据
-
4、@Update注解
修改参数嘛,这个时候记得使用参数占位符或者是直接传递User对象
-
我这里设置了一个方法,通过id修改用户的name和密码
-
测试方法
-
-
查看id为19号的数据是否修改成功
5、@Delete注解
删除
11.4、Mybatis复杂关系映射的开发
1、一对一注解查询@Results&@Result
关于@Results注解,这个就类似原先配置文件当中的resultMap,内部可以存储多个@Result,所以其内部是数组形式的
关于@Result注解,这个注解替代了原先的id标签和result标签,内部依旧有column和property属性
-
查询每条订单所对应的用户
-
SQL语句规划
- 其中,Orders表的uid为users表中的id
-
@Results和@Result注解的配置
-
其内部为一个数组{},写法和原先一样
-
// 参数注入 @Results( // 内部的属性是数组形式的 { @Result(column = "id",property = "id"), @Result(column = "ordertime",property = "ordertime"), @Result(column = "ordermoney",property = "ordermoney"), // 开始配置User用户的数据 @Result(column = "uid",property = "user.id"), @Result(column = "username",property = "user.username"), @Result(column = "password",property = "user.password"), @Result(column = "birthday",property = "user.birthday") } )
-
-
设计查询方法开始测试
-
测试结果为
2、@One、一对一注解查询第二种方式
对于多表查询来说,数据量大的时候,单表查询的效率高于多表查询
为什么这么说,请看效果
-
原SQL语句查询的效果(select * from orders o,users u where o.uid = u.id)
-
单条SQL语句查询的效果
这个暂且一笔带过,复杂的SQL查询配置文件的方式会好一些