一 动态SQL
映射配置文件中可进行SQL语句的动态拼接,需要基于标签:if、where、foreach、sql
- where标签相当于SQL语句中的where关键字,进行条件判断;区别是where本身不包含条件判断,需要内部if标签具体条件判断。
- if标签进行条件判断,可以接在SQL的where关键字之后,也可以放在where标签内;用if标签主要是为了对where关键字下的多条件判断进行扩展
- foreach标签,SQL语句中有些条件涉及容器(如:in),由于MyBatis中传递参数到SQL语句中时没有集合类型,所以需要foreach标签在SQL语句中动态生成集合
<!-- where 关键字 + if 标签:where条件保持true,具体条件将if判断-->
<!-- 注意:if 里面有and,也就是说最后还是字符串拼接-->
<select id="findUserByCondition" resultType="userMap">
select * from user where i = 1
<if test="name != null">
and username = #{userName}
</if>
<if test="id > 10">
and id = #{id}
</if>
</select>
<!-- where标签 + if 标签:sql语句更清晰-->
<!-- 从if里面还是有and来看,似乎只是改由自动生成where 1 = 1了-->
<select id="findUserByCondition" resultType="userMap">
select * from user
<where>
<if test="name != null">
and username = #{userName}
</if>
<if test="id > 10">
and id = #{id}
</if>
</where>
</select>
<!-- foreach标签产生集合:实际还是拼接字符串,open为条件的开始,close为条件的结束-->
<!-- collection表示实体类中被遍历的集合字段,item表示遍历的每项名称,separator表示item拼接字符串时的分隔符-->
<!-- #{item的名称} 这个写法表示遍历-->
<select id="findUserByCondition" resultType="userMap">
select * from user
<where>
<if test="name != null">
<foreach collection="ids" open="and id in (" close=")" item="name" separator=",">
#{name}
</foreach>
</if>
</where>
</select>
二 多表查询
表间关系:
一对一:一个用户一个身份证号
一对多:一个用户下多个订单
多对一:多个订单属于一个用户
多对多:一个用户有多种角色,一种角色属于多个用户【MySQL中多对多关系的实现是通过两个表都对中间表一对多关系】
MyBatis中的关系:
以上关系是逻辑上的关系,MyBatis实现时只有两种关系:一对一关系、一对多关系;
多对一关系中,将“多”的一方中任意一个记录拿出来,与"一"的一方对应,因为“多”中每次拿一个出来时,就是一对一
多对多关系中,将任意一个“多”中的任意一个记录拿出来,与另一个“多”对应,就是一对多关系
举例:
用户和账号:一个用户可有多个账号,一个账号只属于一个用户
用户和角色:一个用户可有多个角色,一个角色下包含多个用户
实现:
- 数据库中关系实现:建立用户表、账户表、角色表、用户角色中间表;账户表外键添加用户表,体现一对多关系;中间表外键分别添加用户表、角色表,实现多对多关系;
- Java程序中关系实现:建立用户实体类、账户实体类、角色实体列;一对一关系映射:从表实体中包含一个主表实体的对象引用;一对多关系映射;主表实体应包含从表实体的集合引用
- 建立用户配置文件(MyBatis映射配置文件)、账户映射配置文件(MyBatis映射配置文件);
- 实现配置:查询用户时,可同时得到用户下的账户信息、角色信息;查询账户时,可同时获取所属用户信息;查询角色时、可获取包含的所有用户信息;
数据表:
user表:id、username
account表:id、uid、money
role表:id、role_name、role_desc
user_role中间表:uid、rid
实体类:
public class User implements Serializable {
private Integer id;
private String name;
// 用于反映与Account的一对多关系
private List<Account> accounts;
// 用于反映与Role的一对多关系
private List<Role> roles;
// get、set、toString略
}
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
// 用于反映与User的一对一关系
private User user;
// get、set、toString略
}
public class Role implements Serializable {
private Integer roleId;
private String roleName;
private String roleDesc;
// 用于反映与User的一对多关系
private List<User> users;
}
接口:
public interface IUserDao{
public List<User> findUserAccounts();
public List<User> findUserRoles();
}
public interface IAccountDao {
// 查询所有账户,并且带有用户信息
public List<Account> findAllWithUser();
}
public interface IRoleDao {
public List<Role> findAllWithUser();
}
2.1 XML配置方式
2.1.1 一对一、一对多操作
映射配置文件
IAccountDao.xml 一对一查询
<!-- 定义封装accout和user的resultMap-->
<!-- account 类型中有个字段为User应用-->
<resultMap id="accountUserMap" type="account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 一对一关系映射:配置封装user的内容,column属性表明基于主表的哪个字段获取-->
<!-- javaType属性表明封装对象为什么Java对象-->
<association property="user" column="uid" javaType="pojo.User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
</association>
</resultMap>
<!-- 一对一:多表查询-->
<select id="findAllWithUser" resultType="accountUserMap">
select a.*,u.username from account a,user a where u.id=a.uid
</select>
IUserDao.xml 一对多查询
<!-- 定义User和Account的resultMap-->
<resultMap id="userAccountMap" type="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<!-- 配置user对象中accounts集合的映射-->
<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="findUserAccounts" resultType="userAccountMap">
select * from user u left outer join account a on u.id= a.uid;
</select>
2.1.2 多对多
多对多要记住:实际是两个表都对中间表进行一对多操作。在Java代码中看不出中间表的存在,但SQL语句基于中间表。
映射配置文件
IRoleDao.xml 一对多查询
<resultMap id="accountUserMap" type="account">
<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>
</collection>
</resultMap>
<!-- sql语句换行计得要加空格-->
<select id="findAllWithUser" resultType="accountUserMap">
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>
IUserDao.xml 一对多查询
<!-- 定义User和Role的resultMap-->
<resultMap id="userRoleMap" type="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<!-- 配置user对象中accounts集合的映射-->
<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="findUserRoles" resultType="userRoleMap">
select u.* , r.id as rid , r.role_name , r.role_desc from user u
left outer join user_role ur on u.id = ur.rid
left outer join user r on r.id = ur.uid;
</select>
2.2 注解方式
@One:从表(一的一方)
public interface IAccountDao {
@Select("select * from account")
@Results(id="accountMap",value = {
@Result(id=true,column = "id",property = "id"),
@Result(column = "uid",property = "uid"),
@Result(column = "money",property = "money"),
// property: 将返回值注入到 user属性
// columns: 根据数据表的uid查找user
// one: Account与要查找的User是一对一的关系【这里一对一指的是,一个Account只能对应上一个User】
// select: 多表查询时,要使用的查询方法,指定User中查询方法的全限定方法名
// FetchType: 多表查询时,延迟加载还是立即加载
@Result(property = "user",column = "uid",one = @One(
select = "IUserDao.findById",
fetchType = FetchType.EAGER
))
})
public List<Account> findAllWithUser();
@Select("select * from account where uid=#{userId}")
public Account findByUid(Integer userId);
}
@Many:主表(多的一方)
public interface IUserDao{
@Select("select * frim user")
@Results(id = "userMap", value = {
@Result(id=true,column = "id",property = "id"),
@Result(column = "name",property = "name"),
@Result(property = "accounts",column = "id",many = @Many(
select = "IAccountDao.findByUid",
fetchType = FetchType.LAZY
))
})
public List<User> findUserAccounts();
public List<User> findUserRoles();
@Select("select * from user where id=#{id}")
public User findById(Integer id);
}
三 事务管理
什么是事务?
事务的四大特性ACID
不考虑隔离性产生的三个问题
解决办法:四种隔离级别
SqlSession的commit方法进行事务提交,rollback方法进行事务回滚
SqlSessionFactory.openSession(),方法参数默认为false,如果设为true,则SqlSession中的每个方法(Dao接口动态代理底层的方法)操作数据库后会自动提交事务,不需要手动 commit。实际开发中不这么做【如转账操作中,多个对数据库的操作合起来才叫一个事务,要一起commit】。