Mybatis_two
SqlMapConfig.xml配置文件
- SqlMapConfig.xml中配置的内容和顺序如下:
- properties(属性)
- settings(全局配置参数)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境集合属性对象)
- environment(环境子属性对象)
- transactionManager(事务管理)
- dataSource(数据源)
- mappers(映射器)
注意:配置文件的标签顺序需严格安装要求.
properties
原配置中:
这部分可以有另外的使用方式:
在同目录下新建一个properties的配置文件
内容如下:
jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql:///mybatisjdbc.username=rootjdbc.password=123
然后配置文件需修改为:
<configuration><!-- 配置properties可以在标签内部配置连接数据库的信息。也可以通过属性引用外部配置文件信息resource属性: 常用的用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下。url属性:是要求按照Url的写法来写地址URL:Uniform Resource Locator 统一资源定位符。它是可以唯一标识一个资源的位置。它的写法:http://localhost:8080/mybatisserver/demo1Servlet协议 主机 端口 URIURI:Uniform Resource Identifier 统一资源标识符。它是在应用中可以唯一定位一个资源的。--><properties resource="jdbcconfig.properties"></properties><!--配置环境--><environments default="mysql"><!-- 配置mysql的环境--><environment id="mysql"><!-- 配置事务 --><transactionManager type="JDBC"></transactionManager><!--配置连接池--><dataSource type="POOLED"><!--当使用外部配置文件是需要用${}引用--><property name="driver" value="${jdbc.driver}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></dataSource></environment></environments>
注意: MyBatis 将按照下面的顺序来加载属性:
- 使用 properties 元素加载的外部属性文件优先级最高。
- 然后会读取properties 元素中resource加载的属性,它会覆盖已读取的同名属性。
typeAliases(类型别名)

</properties><typeAliases><!--用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写--><package name="com.it.domain"></package></typeAliases>
不区分大小写 
mappers(映射器)
原配置信息:

重新配置后:
<mappers><!-- package标签是用于指定dao接口所在的包,当指定了之后就不需要在写mapper以及resource或者class了 --><package name="com.it.dao"></package></mappers>
此配置的前提是:要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

连接池与事务
type=”POOLED”:MyBatis会创建PooledDataSource实例 
结论:它是从池中获取一个连接来用。
底层:PooledDataSource.java
总结:最后我们可以发现,真正连接打开的时间点,只是在我们执行SQL语句时,才会进行。其实这样做我们也可以进一步发现,数据库连接是我们最为宝贵的资源,只有在要用到的时候,才去获取并打开连接,当我们用完了就再立即将数据库连接归还到连接池中。 所以我们使用连接池的技术会节省连接资源。

面试题
为什么MyBatis连接池要设计为有一个空闲连接列表和一个活动连接列表?
- 1:从连接池的设计看,这两个连接列表都是必需的,加起来等于是连接池能用的最大连接数。
- 2:当有新的连接请求时,有从空闲连接列表中选择一个可用的连接,如果这个连接可以正常执行,转移到活动连接列表。
- 3:新建连接比较耗时,所以一开始就新建好一堆连接,这些连接没有被使用的时候就在空闲列表里。
- 4:当要使用的时候,就从空闲列表里拿一个,放到活动列表里。
次:
一:什么是事务? 
二:事务的四大特性ACID? 
三:不考虑隔离性会产生的3个问题?
- 脏读(针对未提交数据)如果一个事务中对数据进行了更新,但事务还没有提交,另一个事务可以“看到”该事务没有提交的更新结果,这样造成的问题就是,如果第一个事务回滚,那么,第二个事务在此之前所“看到”的数据就是一笔脏数据。
- 不可重复读(针对其他提交前后,读取数据本身的对比)不可重复读取是指同一个事务在整个事务过程中对同一笔数据进行读取,每次读取结果都不同。如果事务1在事务2的更新操作之前读取一次数据,在事务2的更新操作之后再读取同一笔数据一次,两次结果是不同的
- 幻读(针对其他提交前后,读取数据条数的对比) 幻读是指同样一笔查询在整个事务过程中多次执行后,查询所得的结果集是不一样的。幻读针对的是多笔记录。如果事务1在事务2的新增操作之前读取一次数据,在事务2的新增操作之后再读取同一笔数据,取得的结果集是不同的,幻读发生。
- 不可重复读和幻读比较: 两者有些相似,但是前者针对的是update或delete,后者针对的insert。
四:解决办法:四种隔离级别?
1、Serializable (串行化):最严格的级别,事务串行执行,资源消耗最大;
2、REPEATABLE READ(重复读) :保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读”和“不可重复读”的情况,但不能避免“幻读”,但是带来了更多的性能损失。(mysql的默认隔离级别)
3、READ COMMITTED (读已提交):大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读”,但不能避免“幻读”和“不可重复读”。该级别适用于大多数系统。(oracle的默认隔离级别)
4、Read Uncommitted(读未提交) :事务中的修改,即使没有提交,其他事务也可以看得到,会导致“脏读”、“幻读”和“不可重复读取”。
总结:如果设置此时事务就设置为自动提交了,同样可以实现CUD操作时记录的保存。虽然这也是一种方式,但就编程而言,设置为自动提交方式为false再根据情况决定是否进行提交,这种方式更常用。因为我们可以根据业务情况来决定业务是否进行提交。如果设置自动提交,如果当前操作有误,事务很难设置回滚了。 只有设置为手动提交,我们才能更好的控制事务。
Mybatis映射文件的SQL深入
动态SQL之标签
第一步:UserDao.xml中配置
<!--根据条件查询--><select id="findByCondition" parameterType="user" resultMap="userMap">select * from user where 1=1<if test="userName != null">and username = #{userName}</if><if test="userSex != null">and sex = #{userSex}</if></select>
第二步:UserDao.java中配置 List<User> findByCondition(User user);
第三步:MybatisTest.java中配置
/*** 测试根据查询条件查询*/@Testpublic void testFindByCondition(){User user = new User();user.setUserName("老王");//user.setUserSex("女");//5.执行查询一个方法List<User> users = userDao.findByCondition(user);for(User u : users){System.out.println(u);}}
注意:
另外要注意where 1=1 的作用~ !
动态SQL之标签
UserDao.xml
<!--where标签的使用(省略where 1=1)--><select id="findByCondition" parameterType="user" resultMap="userMap">select * from user<where><if test="userName != null">and username = #{userName}</if><if test="userSex != null">and sex = #{userSex}</if></where></select>
<where />可以自动处理第一个and
动态SQL标签之标签
第一步:QueryVo.java
public class QueryVo {private User user;private List<Integer> ids = new ArrayList<Integer>();
第二步:UserDao.xml
<!--foreach标签的使用--><select id="findInIds" parameterType="queryVo" resultMap="userMap">select * from user<where><if test="ids != null and ids.size()>0"><foreach collection="ids" open=" and id in (" close=")" item="uid" separator=",">#{uid}</foreach></if></where></select>

这里:
SQL语句: select 字段from user where id in (?) <foreach>标签用于遍历集合,它的属性:
collection:代表要遍历的集合元素,注意编写时不要写#{}
open:代表语句的开始部分
close:代表结束部分
item:代表遍历集合的每个元素,生成的变量名
sperator:代表分隔符
第三步:UserDao.java List<User> findInIds(QueryVo queryVo);
第四步:MybatisTest.java
/*** 测试嵌套子查询forEach*/@Testpublic void testFindInIds(){QueryVo queryVo = new QueryVo();List<Integer> ids = new ArrayList<Integer>();ids.add(41);ids.add(42);ids.add(43);ids.add(46);queryVo.setIds(ids);//5.执行查询一个方法List<User> users = userDao.findInIds(queryVo);for(User u : users){System.out.println(u);}}
Mybatis中简化编写的SQL片段
Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的。
我们先到UserDao.xml文件中使用
<sql id="defaultUser">select * from user</sql><!--foreach标签的使用--><select id="findInIds" parameterType="queryVo" resultMap="userMap"><!--select * from user--><include refid="defaultUser"></include><where><if test="ids != null and ids.size()>0"><foreach collection="ids" open=" and id in (" close=")" item="uid" separator=",">#{uid}</foreach></if></where></select>
其中
注意:如果引用其它mapper.xml的sql片段,则在引用时需要加上namespace,如下: <include refid="namespace.sql片段”/>
意思为当片段不是本文件的,需要用真实路径引用,如: <include refid="com.it.dao.mappers.defaultUser"></include>
Mybatis 的多表关联查询
本次案例主要以最为简单的用户和账户的模型来分析Mybatis 多表关系。用户为User 表,账户为Account 表。一个用户(User)可以有多个账户(Account)。具体关系如下: 
【创建表】【添加数据】
DROP TABLE IF EXISTS `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);
Mybatis维护一对多关系
第三步:创建Account.java
public class Account implements Serializable{private Integer id;private Integer uid;private Double money;// 省略set、get方法}
第四步:创建AccountDao.java
/*** 用户的持久层接口*/public interface AccountDao {/*** 查询所有用户* @return*/List<Account> findAll();/*** 根据id查询用户信息* @param id* @return*/Account findById(Integer id);}
第五步:创建AccountDao.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.it.dao.AccountDao"><!-- 查询所有 --><select id="findAll" resultType="com.it.domain.Account">select * from account;</select><!-- 根据id查询用户 --><select id="findById" parameterType="INT" resultType="com.it.domain.Account">select * from account where id = #{uid}</select></mapper>
第六步:编写测试类MybatisTest.xml
public class MybatisTest {private InputStream in;private SqlSession sqlSession;private UserDao userDao;private AccountDao accountDao;@Before//用于在测试方法执行之前执行public void init()throws Exception{//1.读取配置文件,生成字节输入流in = Resources.getResourceAsStream("SqlMapConfig.xml");//2.获取SqlSessionFactorySqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);//3.获取SqlSession对象sqlSession = factory.openSession();//4.获取dao的代理对象userDao = sqlSession.getMapper(UserDao.class);accountDao = sqlSession.getMapper(AccountDao.class);}}
测试查询所有。
/*** 查询所有账户*/@Testpublic void testFindAllAccount(){//5.执行查询所有方法List<Account> list = accountDao.findAll();for(Account account : list){System.out.println(account);}}
这里别忘记配置:在SqlMapConfig.xml中
<mappers><!--<mapper resource="com/it/dao/UserDao.xml"></mapper>--><!--<mapper resource="com/it/dao/AccountDao.xml"></mapper>--><package name="com.it.dao"></package></mappers>
一对多查询,查询用户,同时查询账号的集合
需求:查询所有用户信息及用户关联的账户信息。
分析:用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,只不过没有信息的字段用null表示,我们想到了左外连接查询比较合适。
第一步:
Sql语句: SELECT u.*, a.id as aid, a.uid, a.money FROM user u LEFT JOIN account a ON u.id = a.uid 
第二步:修改User.java对象
public class User implements Serializable {private Integer id;private String username;private String address;private String sex;private Date birthday;private List<Account> accounts;public List<Account> getAccounts() {return accounts;}public void setAccounts(List<Account> accounts) {this.accounts = accounts;}}
重写toString的方法
@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", address='" + address + '\'' +", sex='" + sex + '\'' +", birthday=" + birthday +", accounts=" + accounts +'}';}
第三步:UserDao.java接口中加入查询方法
在UserDao接口中加入查询方法:public List
/*** 根据用户账号的信息* @param* @return*/List<User> findUserAccountList();
第四步:修改UserDao.xml映射文件
<!--定义用户和账号的查询--><resultMap id="userMap" type="user"><id property="id" column="id"></id><result property="username" column="username"></result><result property="address" column="address"></result><result property="sex" column="sex"></result><result property="birthday" column="birthday"></result><collection property="accounts" ofType="account"><id property="id" column="aid"></id><result property="uid" column="uid"></result><result property="money" column="money"></result></collection></resultMap><!-- 根据用户和账号的信息 --><select id="findUserAccountList" resultMap="userMap">SELECT u.*, a.id as aid, a.uid, a.money FROM user u LEFT JOIN account a ON u.id = a.uid</select>
这里:
collection部分定义了用户关联的账户信息。表示关联查询结果集
property="accounts":关联查询的结果集存储在User对象的上哪个属性。
ofType="account":指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名,即com.it.domain.Account。
第五步:在测试类MybatisTest.java中添加:
/*** 查询所有用户和账号信息*/@Testpublic void testFindUserAccountList(){//5.执行查询所有方法List<User> list = userDao.findUserAccountList();for(User user : list){System.out.println(user);}}
Mybatis维护多对多关系
用户与角色的多对多关系模型如下: 
在 MySQL 数据库中添加角色表,用户角色的中间表。
角色表 
角色用户关联表(中间表) 
创建表:导入数据
DROP TABLE IF EXISTS `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,'校长','管理整个学校');DROP TABLE IF EXISTS `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.java
这里注意,我们使用属性和数据库字段并不相同
public class Role implements Serializable {private Integer roleId;private String roleName;private String roleDesc;}
第四步:创建RoleDao.java
/*** 角色的持久层接口*/public interface RoleDao {/*** 查询所有角色* @return*/List<Role> findAll();/*** 根据id查询角色信息* @param id* @return*/Role findById(Integer id);}
第五步:创建RoleDao.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.it.dao.RoleDao"><resultMap id="roleMap" type="com.it.domain.Role"><id property="roleId" column="rid"></id><result property="roleName" column="role_name"></result><result property="roleDesc" column="role_desc"></result></resultMap><!-- 查询所有--><select id="findAll" resultMap="roleMap">select * from role;</select><!-- 根据id查询用户 --><select id="findById" parameterType="INT" resultMap="roleMap">select * from role where id = #{uid}</select>
第六步:编写测试类MybatisTest.java
/*** 测试mybatis的crud操作*/public class MybatisTest {private InputStream in;private SqlSession sqlSession;private UserDao userDao;private RoleDao roleDao;@Before//用于在测试方法执行之前执行public void init()throws Exception{//1.读取配置文件,生成字节输入流in = Resources.getResourceAsStream("SqlMapConfig.xml");//2.获取SqlSessionFactorySqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);//3.获取SqlSession对象sqlSession = factory.openSession();//4.获取dao的代理对象userDao = sqlSession.getMapper(UserDao.class);roleDao = sqlSession.getMapper(RoleDao.class);}}
测试查询所有。
/*** 测试查询所有角色*/@Testpublic void testFindAllRole(){//5.执行查询所有方法List<Role> roles = roleDao.findAll();for(Role role : roles){System.out.println(role);}}
多对多查询,查询角色,同时关联用户
【需求】:查询所有角色,同时获取角色的所赋予的用户 
第一步:sql语句
SELECT u.*,r.id AS rid,r.role_name,r.role_desc FROM role r
LEFT JOIN user_role ur ON r.ID = ur.rid
LEFT JOIN USER u ON u.id = ur.uid
第二步:修改Role.java
我们加载的信息中不仅有角色信息,同时还要加载关联加载具有该角色的用户信息。因为一个角色,可以分配给多个用户,所以我们可以考虑在 Role 类中加入一个 List
public class Role implements Serializable {private Integer roleId;private String roleName;private String roleDesc;private List<User> users = new ArrayList<User>();public List<User> getUsers() {return users;}public void setUsers(List<User> users) {this.users = users;}}
重写Role对象的toString方法。
@Overridepublic String toString() {return "Role{" +"roleId=" + roleId +", roleName='" + roleName + '\'' +", roleDesc='" + roleDesc + '\'' +", users=" + users +'}';}
第三步:RoleDao.java
/*** 查询所有角色信息,同时查询用户的信息* @param* @return*/List<Role> findRoleUserList();
第四步:RoleDao.xml
<resultMap id="roleMap" type="com.it.domain.Role"><id property="roleId" column="rid"></id><result property="roleName" column="role_name"></result><result property="roleDesc" column="role_desc"></result><collection property="users" ofType="user"><id property="id" column="id"></id><result property="username" column="username"></result><result property="address" column="address"></result><result property="sex" column="sex"></result><result property="birthday" column="birthday"></result></collection></resultMap><!-- 查询所有角色,同时查询所有用户 --><select id="findRoleUserList" resultMap="roleMap">SELECT u.*,r.id AS rid,r.role_name,r.role_desc FROM role rLEFT JOIN user_role ur ON r.ID = ur.ridLEFT JOIN USER u ON u.id = ur.uid</select>
这里需注意:sql语句如果有换行,需要在sql的后面尽量加一个空格。
第五步:MybatisTest.java
/*** 测试查询所有的角色*/@Testpublic void testFindRoleUserList(){//5.执行查询所有方法List<Role> roles = roleDao.findRoleUserList();for(Role role : roles){System.out.println(role);}}
多对多查询,查询用户,同时关联角色
【需求】:查询所有用户,同时获取用户的所赋予的角色
第一步:sql语句
SELECT u.*,r.id AS rid,r.role_name,r.role_desc FROM USER u
LEFT JOIN user_role ur ON u.ID = ur.uid
LEFT JOIN role r ON r.id = ur.rid
第二步:修改User.java
public class User implements Serializable{private Integer id;private String username;private String address;private String sex;private Date birthday;private List<Role> roles = new ArrayList<Role>();public List<Role> getRoles() {return roles;}public void setRoles(List<Role> roles) {this.roles = roles;}}
重写User对象的toString方法。
@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", address='" + address + '\'' +", sex='" + sex + '\'' +", birthday=" + birthday +", roles=" + roles +'}';}
第三步:UserDao.java
/*** 查询所有的用户,同时查询关联的角色信息* @param* @return*/List<User> findUserRoleList();
第四步:UserDao.xml
<resultMap id="userMap" type="user"><id property="id" column="id"></id><result property="username" column="username"></result><result property="address" column="address"></result><result property="birthday" column="birthday"></result><result property="sex" column="sex"></result><collection property="roles" ofType="role"><id property="roleId" column="rid"></id><result property="roleName" column="role_name"></result><result property="roleDesc" column="role_desc"></result></collection></resultMap><!-- 查询所有的用户,同时查询关联的角色信息 --><select id="findUserRoleList" resultMap="userMap">SELECT u.*,r.id AS rid,r.role_name,r.role_desc FROM USER uLEFT JOIN user_role ur ON u.ID = ur.uidLEFT JOIN role r ON r.id = ur.rid</select>
这里需注意:sql语句如果有换行,需要在sql的后面尽量加一个空格。
第五步:MybatisTest.java
/*** 测试查询所有的用户*/@Testpublic void testFindUserRoleList(){//5.执行查询所有方法List<User> users = userDao.findUserRoleList();for(User user : users){System.out.println(user);}}



浙公网安备 33010602011771号