目录
一,MyBatis 连接池的配置
事务
二,动态SQL语句
动态SQL语句之if标签
演示if标签
动态SQL语句之foreach标签
foreach标签演示
Set 标签
三,抽取重复的SQL代码片段
四,多表查询
1.实体之间的关系
2.多表查询准备步骤
3.建立两张表一张用户表一张账户表
4.建立两张表对应的实体类
5.提供用户和账户的接口
6.提供用户和账户的映射文件
7.编写核心配置文件
8.我们先单表测试一下查询所有账户
9.当我们单表查询测试没有问题后,我们接下来进行多表查询
10.我们看一下在程序中进行查询,查询该账户所对应的用户信息,该怎么配置和编写
11. 接下来,再账户的接口中提供相应的方法
12.在账户的映射文件中编写多表查询的配置
13.测试
14.说明:采用这种继承的方式进行多表查询不常用,仅作了解
使用开发中常用的方式进行多表查询
1.首先我们在多表对应的实体类中,维护一个单表一方的实体类对象
2.在多表实体类的 IAccountDao.xml 映射文件中,进行一对一关系的配置
多表查询之一对多的查询,也就是一个用户可以有很多个账户
1.我在单表实体类中提供一个集合来描述这个一对多的关系,集合里面装的就是多个账户
2.在单表实体类的 IUserDao.xml 映射文件中,进行配置
3.编写sql语句,查询所有用户对应的账户信息,如果该用户没有账户信息以null展示
4. 修改查询所有用户的配置,把返回类型修改为我们配置的映射关系,以及把sql语句粘贴过来
5.测试
多表查询之多对多
1.多对多的处理
2.建立两张表:用户表,角色表
3.建立用户和角色的实体类
4.提供用户和角色的接口
5.提供接口对应的映射文件
6.核心配置文件还是之前的,就不用动了
7.下来,我们查询所有角色有哪些用户在使用
8.sql 语句编写好后,我们在实体类中来体现多对多的关系
9.在角色的接口中提供方法
10.在角色接口的 IRoleDao.xml 映射文件中进行配置
11.把配置查询的返回类型,以及sql语句
12.测试
下来就是查询该用户有哪些角色
1.在用户的接口中提供方法
2.在用户的实体类中,维护一个集合,用来描述多对多的关系
3.修改SQL语句
4.在IUserDao.xml 映射文件中编写配置
5.测试
连接池:相当于一个容器,会提前初始化一定数量的连接对象,以便重复使用 1、我们在实际开发中都会使用连接池。因为它可以减少我们获取连接所消耗的时间。 mybatis中的连接池 mybatis连接池提供了3种方式的配置: 配置的位置: 主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式。 type属性的取值: POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现 UNPOOLED 采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池的思想。
mybatis中的事务 什么是事务(很多步骤,是一个整体不可再分割,组成事务的每一个步骤,要么同时成功,要么同时失败) 事务的四大特性ACID 不考虑隔离性会产生的3个问题 解决办法:四种隔离级别 mybatis中的事务 它是通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚 如果我们不想手动提交事务,可以在创建SqlSession对象时传入true,表示自动提交事务 SqlSession session = factory.openSession(true);
Mybatis 的动态 SQL 语句 Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的, 有些时候业务逻辑复杂时,我们的 SQL 是动态变 化的, 此时在前面的学习中我们的 SQL 就不能满足要求了
我们根据实体类的不同取值,使用不同的 SQL 语句来进行查询。 比如在 id 如果不为空时可以根据 id 查询, 如果 username 不同空时还要加入用户名作为条件。 这种情况在我们的多条件组合查询中经常会碰到。 注意:标签的 test 属性中写的是对象的属性名,如果是包装类的对象要使用 OGNL 表达式的写法。 另外要注意 where 1=1 的作用~
1.在接口中提供一个方法,根据性别和年龄去查询用户 public interface IUserDao { //根据姓名和性别去查询用户 ListselectUserByNameAndSex(User user); } 2.在映射文件中进行配置 说明: 标签的 test 属性中写的是对象的属性名 3.测试 : @Test public void testLikeByVO() { IUserDao dao = sqlSession.getMapper(IUserDao.class); User user = new User(); user.setUsername("沈某某"); user.setSex("男"); List list = dao.selectUserByNameAndSex(user); for (User user1 : list) { System.out.println(user1); } } 4:问题:就是单纯用if标签来拼接条件,我们在写条件的时候,得把 where 1=1 写上 麻烦 那能不能 不写where 和 1=1 呢? 可以 那就得使用一个标签叫where标签 5.所以我们可以写成下面这样
传入多个 id 查询用户信息,用下边两个 sql 实现: SELECT * FROM USERS WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id=16) SELECT * FROM USERS WHERE username LIKE '%张%' AND id IN (10,89,16) 这样我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。 这样我们将如何进行参数的传递? 在 QueryVo 中加入一个 List 集合用于封装参数 public class QueryVo implements Serializable { private Listids; }
1.在接口中提供一个根据多个id查询出多个用户 public interface IUserDao { //根据多个id查询出多个用户 ListselectUserByIDS(QueryVO vo); //说明:我们的传入参数是QueryVO 也就是说,我们在QueryVO中提供一个集合,集合里面添加多个id } 2.在QueryVO 中提供一个属性 是集合类型的 public class QueryVO { User user; List list; //get set 方法略 自己补上 } 3.在映射文件中编写配置 foreach标签中的属性说明: collection:代表要遍历的集合元素,注意编写时不要写#{} open:代表语句的开始部分 close:代表结束部分 item:当前遍历的集合中的元素 separator:分隔符 4.测试: @Test public void testUerByIDS() { IUserDao dao = sqlSession.getMapper(IUserDao.class); List list = Arrays.asList(50, 51, 52); QueryVO queryVO = new QueryVO(); queryVO.setList(list); List users = dao.selectUserByIDS(queryVO); for (User user : users) { System.out.println(user); } } 5.当然严谨期间你可以在遍历集合的时候,使用if标签对集合进行判断,当集合长度大于0并且集合不为空再去遍历集合
UPDATE t_user name = #{name}, age = #{age}, AND id = #{id}
我们将sql中的set去掉了,加了个set元素,set元素会内部嵌套的sql进行处理,将这部分sql前后的逗号给去掉并在前面加上set
。
当预期id和age的时候,生成的sql:
UPDATE t_user SET age = ? where id = ?
1.我们在映射文件中编写增删改查的配置时,写的一些SQL语句有一些片段是重复的 比如 select * from user 那么我们可以把这些重复的sql代码抽取处理,就可以 复用 2. 我们在映射文件中可以使用一个sql标签来抽象重复的sql代码 select * from user 3.在其他地方,有使用到select * from user 这条语句的地方,就可以通过id来引用,引用要使用一个标签 在比如下面也可以引用sql代码片段
表之间的关系有几种:一对多,多对一 ,一对一,多对多 举例: 用户和订单就是一对多 订单和用户就是多对一 一个用户可以下多个订单 多个订单属于同一个用户 人和身份证号就是一对一 一个人只能有一个身份证号 一个身份证号只能属于一个人 老师和学生之间就是多对多 一个学生可以被多个老师教过 一个老师可以交多个学生 特例: 用户和订单就是一对多 如果拿出每一个订单,他都只能属于一个用户。 所以Mybatis就把多对一看成了一对一。
示例:用户和账户 一个用户可以有多个账户 一个账户只能属于一个用户(多个账户也可以属于同一个用户) 步骤: 1、建立两张表:用户表,账户表 让用户表和账户表之间具备一对多的关系:需要使用外键在账户表中添加 2、建立两个实体类:用户实体类和账户实体类 让用户和账户的实体类能体现出来一对多的关系 3、建立两个配置文件 用户的配置文件 账户的配置文件 4、实现配置: 当我们查询用户时,可以同时得到用户下所包含的账户信息 当我们查询账户时,可以同时得到账户的所属用户信息
用户表我们在第一天的时候,已经建立好,账户表,那第一天的资料中有 那么用户表和账户表之间是一对用的关系,且添加了外键约束
public class User implements Serializable { private Integer id; private String username; private Date birthday; private String sex; private String address; //get set 方法略 自己补上 } public class Account implements Serializable { private Integer id; private Integer uid; private Double money; //get set 方法略 自己补上 } 注意:实体类的属性名和表的字段名保持一致
//提供用户的接口,把我们前的操作保留两个 public interface IUserDao { //查询所有用户 ListfindAll(); //根据id查询一个用户 User selectUserById(Integer id); } //提供账户的接口 public interface IAccountDao { //等会再添加增删改查的操作的方法 }
//用户的映射文件 //账户的映射文件 注意:映射文件所在的文件夹层次结构,要和接口的包名保持一致,映射文件名和接口名保持一致
注意:核心配置文件,引入dao接口的映射文件采用扫描对应包下的所有接口
1.我们在账户的接口中提供查询所有账户信息的方法 public interface IAccountDao { ListfindAll(); } 2. 编写账户的映射文件 3.测试:单独写一个测试Account的测试类,使用@Before @After 注解来加载配置文件和释放资源 @Test public void testFindAll(){ IAccountDao dao = sqlSession.getMapper(IAccountDao.class); List list = dao.findAll(); for (Account account : list) { System.out.println(account); } }
1.我们要查询 所有账户所对应的用户信息 我们先在小海豚中编写sql语句测试一下 SELECT a.*,u.* FROM account a,USER u WHERE a.`UID`=u.`id`; 通过上面的查询语句就可以查询出账户所属的用户信息 但是 账户表里有个字段叫 id 用户表里有个字段叫id 重名了,那我们可以给账户表里的id字段起个别名 或者我们不展示用户表里的id字段 也可以 可以写成下面的 SELECT a.*,u.`username`,u.`address` FROM account a,USER u WHERE a.`UID`=u.`id`; 也就是 用户表中,我只展示了姓名和地址两个字段,说明问题就行了
1.因为我们多表查询出的数据既有账户信息还有用户信息,那么得将所有信息封装到一个实体类中,那账户的实体类 中没有用户的信息的字段,所以我们提供一个实体类,继承账户的实体,然后在子类里面,提供封装用户信息的字段 public class AccountUser extends Account{ //我提供了两个字段,因为我们等会查询的时候,只查询user表的两个字段,当然你要查几个字段,你就在实体类中提供几个字段,我提供两个说明问题就行了 private String username; private String address; //get set 方法略,自己补上 //这里提供toString()方法,里面再调用一下父类的toString()方法,等会测试打印的时候,可以看到所有字段的信息 @Override public String toString() { //调用一下父类的toString() return super.toString()+" AccountUser{" + "username='" + username + '\'' + ", address='" + address + '\'' + '}'; } }
public interface IAccountDao { //查询所有账户信息 ListfindAll(); //查询该账户所对应的用户信息 注意范型,是Account的子类,因为他里面的属性,加上从父类继承下来属性,就能封装下我们查询出来的数据 List findAccountAndUser(); }
注意:返回类型是Account的子类,还有sql语句的编写
@Test public void testFindAccountAndUser() { IAccountDao dao = sqlSession.getMapper(IAccountDao.class); Listlist = dao.findAccountAndUser(); for (AccountUser account : list) { System.out.println(account); } }
1.采用继承的方式,不推荐使用。那么我们怎么能体现出这种一对一的关系呢? 2.我们在实体类中来体现这种一对一的关系
public class Account implements Serializable { private Integer id; private Integer uid; private Double money; //在多表实体类中,维护单表实体类的对象 private User user; //get set 方法省略 自己补上 }
1.在配置之前,我们现在小海豚中编写sql语句测试一下 那么这次我们可以把 用户表的所有字段都展示完,账户表中的一个id字段名和用户表中的id字段名一样, 我们给账户表中的id字段起个别名,sql语句如下: SELECT a.id AS aid,a.uid,a.money,u.* FROM account a,USER u WHERE a.`UID`=u.`id`; 我们在映射文件中 就可以使用上面这条sql语句 2.接下来在多表实体类的映射文件中进行一对一映射关系的配置 * 先使用resultMap表签 配置多表和实体类的映射关系 * 然后使用association这个标签来配置一对一 3. 修改查询所有账户的配置的返回值类型,修改为我们配置的映射关系 resultMap="myAccountMap" 4.修改sql语句 5.测试:通过account.getUser();可以查看该账户对应的用户信息 @Test public void testFindAll(){ IAccountDao dao = sqlSession.getMapper(IAccountDao.class); List list = dao.findAll(); for (Account account : list) { System.out.println(account); //获取该账户对应的用户信息 User user = account.getUser(); System.out.println(user); } }
public class User implements Serializable { private Integer id; private String username; private Date birthday; private String sex; private String address; /*在单表实体类中提供集合,来描述一对多的关系*/ private Listaccounts; //get set 方法略 自己补上 }
* 使用resultMap标签 配置实体类和表的映射关系 * 使用collection标签 配置集合 collection标签 有两个属性 1. property 主表实体类中维护的那个集合 2. ofType 集合中装的什么类型
在小海豚中编写sql语句测试好 -- 我要查询所有用户的账户信息,没有账户信息的以null展示,但是所有用户信息必须展示出来, -- 内连接不行,因为内连接不符合条件的是不展示 -- 所以我们使用外连接 SELECT u.*,a.id AS aid,a.`MONEY`,a.`UID` FROM USER u LEFT OUTER JOIN account a ON u.`id`=a.`UID`;
修改返回值类型 改为 resultMap="userMap"
@Test public void testFindAll() throws IOException { //获取接口的代理对象 IUserDao userDao = sqlSession.getMapper(IUserDao.class); //执行查询操作 Listlist = userDao.findAll(); for (User user : list) { System.out.println(user); //获取该用户对应的账户信息 List accounts = user.getAccounts(); System.out.println(accounts); } }
多对多的处理 示例:用户和角色 一个用户可以有多个角色 一个角色可以赋予多个用户 步骤: 1、建立两张表:用户表,角色表 让用户表和角色表具有多对多的关系。需要使用中间表,中间表中包含各自的主键,在中间表中是外键。 2、建立两个实体类:用户实体类和角色实体类 让用户和角色的实体类能体现出来多对多的关系 各自包含对方一个集合引用 3、建立两个配置文件 用户的配置文件 角色的配置文件 4、实现配置: 当我们查询用户时,可以同时得到用户所包含的角色信息 当我们查询角色时,可以同时得到角色的所赋予的用户信息
因为用户和角色是多对多的关系,所以还需要建立一张中间表 建表语句,在第一天的资料中有,这里就省略了
//用户的实体类 public class User implements Serializable { private Integer id; private String username; private Date birthday; private String sex; private String address; //get set 方法略 自己补上 } //角色的实体类 public class Role implements Serializable { private Integer id; private String roleName; private String roleDesc; //get set 方法略 自己补上 } 注意:这里我故意没让角色实体类的属性名和表中的字段名保持一致,也就是提醒我们要配置映射
public interface IUserDao { //查询所有用户 ListfindAll(); } public interface IRoleDao { //查询所有角色对应的用户信息 List findAll(); }
//里面等会再来编写
1.我们现在小海豚中,把sql语句先测试好 我们一步一步来编写sql语句 * 先查把角色表和中间表关联起来查询 SELECT r.*,ur.* FROM role r LEFT OUTER JOIN user_role ur ON r.`id`=ur.`RID`; * 接着,上一步查询的数据里面还没有 用户的信息,所以再继续加入 user表查询 SELECT r.*,u.* FROM role r LEFT OUTER JOIN user_role ur ON r.`id`=ur.`RID` LEFT OUTER JOIN USER u ON u.`id`=ur.`UID` 注意:中间表的字段信息,不用,所以把ur.* 删掉,把 u.* 用户表的信息加上,中间表只是作为连接条件 * 上面第二步查询出的字段中 这个角色表的id 和用户表的 id 重名了,所以我们给角色表的id字段起个别名 所以sql 语句编写如下: SELECT r.id AS rid,r.`ROLE_NAME`,r.`ROLE_DESC`,u.* FROM role r LEFT OUTER JOIN user_role ur ON r.`id`=ur.`RID` LEFT OUTER JOIN USER u ON u.`id`=ur.`UID`
1.我们在角色的实体类中提供一个集合,用来描述多对多的关系 public class Role implements Serializable { private Integer id; private String roleName; private String roleDesc; //提供一个集合,来描述多对多的关系,集合里面放的是User类 private Listusers; //get set 方法略 自己补上 }
public interface IRoleDao { //查询所有角色对应的用户信息 ListfindAll(); }
* 配置角色实体类和角色表的映射关系,注意我的角色实体类中的属性名没有和表中的字段名保持一致,所以配的时候注意 collection 标签中的两个属性: * property="users" 角色表中维护的那个集合对象 * ofType="org.westos.domain.User" 集合中装的什么类型
1.把我们刚才测试好的sql语句粘贴进来,以及返回类型修改为我们配置的映射id名
* @Before @After 中的代码略了 ,注意一下 @Test public void testFindAll(){ IRoleDao dao = sqlSession.getMapper(IRoleDao.class); Listlist = dao.findAll(); for (Role role : list) { System.out.println(role); //获取角色的所属用户 List users = role.getUsers(); System.out.println(users); } }
public interface IUserDao { //查询该用户都有哪些角色 ListfindAll(); }
public class User implements Serializable { private Integer id; private String username; private Date birthday; private String sex; private String address; //提供一个集合用来描述一个用户有多个角色 private Listroles; //get set 方法略,自己补上 }
把我们刚才查询该角色属于哪个用户用的左外连接,修改成右外连接, 就可以查询出,所有用户有哪些角色 SELECT r.id AS rid, r.`ROLE_NAME`, r.`ROLE_DESC`, u.* FROM role r RIGHT JOIN user_role ur ON r.`ID` = ur.`RID` RIGHT JOIN USER u ON ur.`UID` = u.`id`
@Test public void testFindAll() throws IOException { //获取接口的代理对象 IUserDao userDao = sqlSession.getMapper(IUserDao.class); //执行查询操作 Listlist = userDao.findAll(); for (User user : list) { System.out.println(user); //获取该用户右哪些角色 List roles = user.getRoles(); System.out.println(roles); } }