Mybatis学习之路 第三篇
- 在第前两篇的基础上,我们继续学习,增加新的功能。
- 本文涉及以下内容:
1. Mybatis连接池介绍 。
2. Mybatis事务自动提交的方法 。
3. Mybatis的动态SQL语句。
4. SQL语句抽取重复代码(了解即可)。
5. Mybatis中的多表查询(一对一,一对多,多对多)。
–
1. Mybatis连接池介绍 |
1.1 Mybatis连接池提供了3种方式的配置:
- 配置的位置:主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式
- type属性的取值:
- POOLED:采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现。
- UNPOOLED:采用传统的获取连接的方式,虽然也实现了javax.sql.DataSource接口,但是并没有使用池的思想。
- JNDI:采用服务器提供的INDI技术实现,来获取DataSource对象,不同服务器所能拿到的DataSource是不一样的。
- 注意:如果不是web或者maven的war工程是不能使用的。
- 我们使用的是tomcat服务器,采用的连接池就是dbcp连接池。
2. Mybatis事务自动提交的方法 |
- 我们在进行CUD操作的过程中,我们都要手动进行事务的提交,原因是 setAutoCommit()方法,在执行时它的值被设置为 false 了,所以我们在 CUD 操作中,必须通过 sqlSession.commit()方法来执行提交操作。
- openSessin()是重载方法,有个参数可以设置是否自动提交的,赋值true即为自动提交。
@Before //用于在测试方法执行之前执行
public void init() throws Exception {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产SqlSession对象
sqlSession = factory.openSession(ture);
//4.使用SqlSession创建Dao接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
3. Mybatis的动态SQL语句 |
3.1 if条件语句
- 在IUserDao中添加如下查询接口代码:
/**
* 根据传入的参数条件查询
*查询条件:有可能有用户名,性别等,有可能都有
*/
List<User> findUserByCondition(User user);
- 在IUserDao.xml中添加相应片段:
<!--根据条件查询-->
<!--这里使用的user是因为声明了别名的-->
<select id="findUserByCondition" resultType="User" parameterType="User">
select * from user where 1=1
<if test="username != null">
and username = #{username}
</if>
<if test="sex != null">
and sex = #{sex}
</if>
</select>
- 在测试类中测试:
@Test
public void testFindByCondition() {
User u = new User();
u.setUsername("老王");
List<User> users = userDao.findUserByCondition(u);
for (User user : users) {
System.out.println(user);
}
}
3.2 动态标签之< where>标签
- 为了简化上面where 1=1 的条件拼装,我们可以采用标签来简化开发。
- 在IUserDao.xml中代码如下,去掉了where 1=1 ,添加标签:
<!--根据条件查询--> <!--这里使用的user是因为声明了别名的--> <select id="findUserByCondition" resultType="User" parameterType="User"> select * from user <where> <if test="username != null"> and username = #{username} </if> <if test="sex != null"> and sex = #{sex} </if> </where>> </select>
3.3 动态标签之< foreach>标签
- 当我们需要传入多个id查询用户信息时,比如下面这条sql语句,我们就需要将一个集合中的值,作为参数动态添加进来。
select * from user where id in(41,42,43,46);
- 在QueryVo中加入一个List集合用于封装参数
public class QueryVo implements Serializable { private List<Integer> ids; public List<Integer> getIds() { return ids; } public void setIds(List<Integer> ids) { this.ids = ids; } }
- 在IUserDao中添加如下:
/** * 根据QueryVo中提供的id集合,查询用户信息 * @return */ List<User> findUserInIds(QueryVo vo);
- 在IUserDao.xml中添加相应片段:
<!--根据QueryVo中Id集合实现查询用户列表--> <select id="findUserInIds" resultType="User" parameterType="QueryVo"> select * from user <where> <if test="ids != null and ids.size()>0"> <foreach collection="ids" open="and id in (" close=")" item="id" separator=","> <!--#{id}和iteam中的值保持一致--> #{id} </foreach> </if> </where> </select>
- 在测试类中测试:
@Test public void testFindInIds() { QueryVo vo = new QueryVo(); ArrayList<Integer> list = new ArrayList<Integer>(); list.add(41); list.add(42); list.add(46); vo.setIds(list); //执行查询所有方法 List<User> users = userDao.findUserInIds(vo); for (User user : users) { System.out.println(user); } }
4. 抽取重复代码(了解即可) |
1. 如果在IUserDao.xml中有很多重复的SQL语句代码,我们就可以使用抽取代码方法了。
- 原来的代码块如下:
<!-- 配置查询所有--> <select id="findAll" resultType="User"> select * from user </select>
- 使用代码抽取后如下,在需要的地方直接引用就可以啦
<!--抽取重复的代码--> <sql id="defaultUser"> select * from user </sql> <!-- 配置查询所有--> <select id="findAll" resultType="User"> <include refid="defaultUser"/> <!--select * from user--> </select>
5. Mybatis中的多表查询 |
数据库的多表查询3中方式:
- inner join:(等值连接) 只返回两个表中联结字段相等的行。(可省略不写,用逗号代替)
- left join:或left outer join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录。
- right join:或right outer join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录。
4.1 一对一查询(多对一)
- 需求:查询所有账户信息,关联查询下单用户信息。
- 注意:因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询。如果从用户信息出发查询用户下的账户信息则为一对多查询,因为一个用户可以有多个账户。
- Mysql中的user表和acount表
- 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-04 11:34:34','女','北京'),(43,'老王','2018-03-07 17:37:26','男','北京');
- account表
CREATE TABLE `account` ( `ID` int(11) NOT NULL COMMENT '编号', `UID` int(11) default NULL COMMENT '用户编号', `MONEY` double default NULL COMMENT '金额', PRIMARY KEY (`ID`), KEY `FK_Reference_8` (`UID`), CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into `account`(`ID`,`UID`,`MONEY`) values (1,46,1000),(2,45,1000),(3,46,2000);
- 编写Account类如下,get、set和toString方法省略了,自行添加:
public class Account implements Serializable { private Integer id; private Integer uid; private Double money; }
4.1.1 通过继承Account子类的方式实现:
- 编写继承Account类的AccountUser类,省略get、set和toString方法,toString方法中加上super.toString():
public class AccountUser extends Account { private String username; private String address; }
- 编写IAccountDao接口方法:
public interface IAccountDao { /** * 查询所有账户,同时获取账户的所属用户信息 * @return */ List<Account> findAll(); }
- 编写IAccountDao.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="fang.dao.IAccountDao"> <!-- 查询所有 --> <select id="findAllAccount" resultType="AccountUser"> select a.*, u.username, u.address from account a , user u where u.id = a.uid; </select> </mapper>
- 编写AccountTest测试类,运行即可:
@Test public void testFindAllAccountUser() { List<AccountUser> aus = accountDao.findAllAccount(); for(AccountUser au: aus) { System.out.println(au); } }
4.1.2 使用resultMap,专门定义的resultMap用于映射一对一查询结果
- 从表实体应该包含一个主表实体的对象引用
- 修改Account类,省略了get、set和toString方法:
public class Account implements Serializable { private Integer id; private Integer uid; private Double money; //从表实体应该包含一个主表实体的对象引用 private User user; }
- 增加IAccountDao中的方法:
/** * 查询所有账户,同时还要获取到当前账户的所属用户信息 * @return */ List<Account> findAll();
- 增加IAccountDao.xml中的内容:
<!-- 定义封装account和user的resultMap --> <resultMap id="accountUserMap" type="account"> <!--aid是sql语句中取的别名,若没有取别名直接id就可以--> <!--sql语句取别名主要因为多个表有相同名称的字段,不取会出现意想不到的问题的--> <id property="id" column="aid"></id> <result property="uid" column="uid"></result> <result property="money" column="money"></result> <!-- 一对一的关系映射:配置封装user的内容--> <!--property是在Account类中定义的要关联的属性,private User user,就是这个user。--> <!--javaType是关联的类型,fang.domain.User--> <association property="user" javaType="user"> <id property="id" column="id"></id> <result column="username" property="username"></result> <result column="address" property="address"></result> <result column="sex" property="sex"></result> <result column="birthday" property="birthday"></result> </association> </resultMap> <!-- 查询所有 --> <select id="findAll" resultMap="accountUserMap"> select u.*,a.id as aid,a.uid,a.money from account a , user u where u.id = a.uid; </select>
- 编写AccountTest测试类:
//测试查询所有 @Test public void testFindAll(){ List<Account> accounts = accountDao.findAll(); for(Account account : accounts){ System.out.println("--------每个account的信息------------"); System.out.println(account); System.out.println(account.getUser()); } }
4.2 一对多查询(多对一)
- 一个用户可能有多个账号,列出所有用户,以及用户关联的账户。(和用户有关联的账户才会被列出)
- 修改User类如下,省略get,set,toString方法:
public class User implements Serializable { private Integer id; private String username; private Date birthday; private String sex; private String address; private List<Account> accounts; }
- 添加IUserDao.xml中的内容:
<resultMap id="userAccountMap" type="user"> <id property="id" column="id"/> <result property="username" column="username"/> <result property="address" column="address"/> <result property="sex" column="sex"/> <result property="birthday" column="birthday"/> <!--collection 是用于建立一对多中集合属性的对应关系ofType 用于指定集合元素的数据类型--> <collection property="accounts" ofType="Account"> <id property="id" column="aid"/> <result property="uid" column="uid"/> <result property="money" column="money"/> </collection> </resultMap> <select id="findAll" resultMap="userAccountMap"> select u.*,a.id as aid ,a.uid,a.money from user u left outer join account a on u.id =a.uid </select>
- 在IUserDao接口中添加如下代码:
/** * 查询所有用户 * @return */ List<User> findAll();
- 在UserTest中编写测试类:
@Test public void testFindAll() { List<User> users = userDao.findAll(); for(User user: users) { System.out.println("===================="); System.out.println(user); System.out.println(user.getAccounts()); } }
4.2 多对多查询
4.2.1 实现Role到User多对多
- 有三张表,分别是用户表,角色表和用户角色(中间表)。
- Mysql中的role表和user_role表
- role表
CREATE TABLE `role` ( `ID` int(11) NOT NULL COMMENT '编号', `ROLE_NAME` varchar(30) default NULL COMMENT '角色名称', `ROLE_DESC` varchar(60) default NULL COMMENT '角色描述', PRIMARY KEY (`ID`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into `role`(`ID`,`ROLE_NAME`,`ROLE_DESC`) values (1,'院长','管理整个学院'),(2,'总裁','管理整个公司'),(3,'校长','管理整个学校');
- user_role表
CREATE TABLE `user_role` ( `UID` int(11) NOT NULL COMMENT '用户编号', `RID` int(11) NOT NULL COMMENT '角色编号', PRIMARY KEY (`UID`,`RID`), KEY `FK_Reference_10` (`RID`), CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`), CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into `user_role`(`UID`,`RID`) values (41,1),(45,1),(41,2);
-
编写Role类。注意这里用的属性名称和数据库不一样哦。同样省略get、set和toString方法:
public class Role implements Serializable { private Integer roleId; private String roleName; private String roleDesc; //多对多的关系映射:一个角色可以赋予多个用户 private List<User> users; }
-
编写IRoleDao接口如下:
public interface IRoleDao { /** * 查询所有角色 * @return */ List<Role> findAll(); }
-
编写IRoleDao.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="fang.dao.IRoleDao"> <resultMap id="roleMap" type="Role"> <id property="roleId" column="rid"/> <!--数据库中定义的是ROLE_NAME,因为windos的mysql不区分大小写,所以可以小写。linux区分不行--> <result property="roleName" column="role_name"/> <result property="roleDesc" column="role_desc"/> <collection property="users" ofType="User"> <id property="id" column="id"/> <result property="username" column="username"/> <result property="address" column="address"/> <result property="sex" column="sex"/> <result property="birthday" column="birthday"/> </collection> </resultMap> <select id="findAll" resultMap="roleMap"> select u.*,r.id as rid,r.role_name,r.role_desc from role r left outer join user_role ur on r.id = ur.rid left outer join user u on u.id = ur.uid </select> </mapper>
-
编写RoleTest测试方法:
@Test public void testFindAll(){ List<Role> roles = roleDao.findAll(); for(Role role : roles){ System.out.println("================"); System.out.println(role); System.out.println(role.getUsers()); } }
4.2.1 实现User到Role多对多
- User到Role的多对多和Role到User的多对多是一样的方式,这里不再列举啦。