MyBatis 学习2
MyBatis 的动态 SQL 语句
<if>
我们根据实体类的不同取值,使用不同的 SQL 语句来进行查询。比如在 id 如果不为空时可以根据 id 查询, 如果 username 也不为空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。
数据库
User 实体类
public class User implements Serializable {
private int userId;
private String userName;
private Date userBirthday;
private String userAddress;
//省略 getter、setter、toString 方法
}
IUserDao.xml
<!--多条件组合模糊查询-->
<select id="findByUser" resultMap="userMap" parameterType="user">
select * from t_user where 1=1
<if test="userName != null">
and username like #{userName}
</if>
<if test="userAddress != null">
and address like #{userAddress}
</if>
</select>
测试方法
//多条件模糊查询
@Test
public void findByUser(){
User user = new User();
user.setUserName("%t%");
user.setUserAddress("%广东%");
List<User> all = userDao.findByUser(user);
for (User u:all){
System.out.println(u);
}
}
<where>
之前写 SQL 语句是这样的
<!--多条件组合模糊查询-->
<select id="findByUser" resultMap="userMap" parameterType="user">
select * from t_user where 1=1
<if test="userName != null">
and username like #{userName}
</if>
<if test="userAddress != null">
and address like #{userAddress}
</if>
</select>
要加个 where 1=1 ,写多了就麻烦,就也可以用 <where> 标签
<select id="findByUser" resultMap="userMap" parameterType="user">
select * from t_user
<where>
<if test="userName != null">
and username like #{userName}
</if>
<if test="userAddress != null">
and address like #{userAddress}
</if>
</where>
</select>
<foreach>
使用多个 id 查询时的 SQL 语句时我们可能会这样写:
SELECT * FROM t_user WHERE id IN (36,39)
给了个范围 36,39看看有没有 id =36 或者 39,那么这样我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。
在 QueryVO 中加入一个 List 集合用于封装参数
public class QueryVO implements Serializable {
private User user;
private List<Integer> ids;
//省略 getter、setter 方法
}
IUserDao.xml
<!--根据所给 id 范围查询-->
<select id="findByIds" resultMap="userMap" parameterType="queryVO">
select * from t_user
<where>
<if test="ids != null and ids.size>0">
<foreach collection="ids" open=" id in (" item="id" close=")" separator=",">
<!--item属性值与 #{这里的值要一样},如这里2个都为 id -->
#{id}
</foreach>
</if>
</where>
</select>
- collection:代表要遍历的集合元素,注意编写时不要写#{}
- open:代表语句的开始部分
- close:代表结束部分
- item:代表遍历集合的每个元素,生成的变量名
- sperator:代表分隔符
测试方法,这里查询id 是在(36,39) 这2个数的数据
//在所给的 id 范围查询
@Test
public void findByIds(){
QueryVO queryVO = new QueryVO();
List<Integer> ids = new ArrayList<Integer>();
ids.add(36);
ids.add(39);
queryVO.setIds(ids);
List<User> byIds = userDao.findByIds(queryVO);
for (User u:byIds){
System.out.println(u);
}
}
<sql>
SQL 中可将重复的 SQL 提取出来,使用时用 include 引用即可,最终达到 SQL 重用的目的。
<!--重用sql语句-->
<sql id="defaultFind">
select * from t_user
</sql>
要用到 select * from t_user 的地方引用一下就可以了
<!--根据所给 id 范围查询-->
<select id="findByIds" resultMap="userMap" parameterType="queryVO">
<!--select * from t_user-->
<include refid="defaultFind"></include>
<where>
<if test="ids != null and ids.size>0">
<foreach collection="ids" open=" id in (" item="id" close=")" separator=",">
<!--item属性值与 #{这里的值要一样},如这里2个都为 id -->
#{id}
</foreach>
</if>
</where>
</select>
多表查询
多表关系
用户和订单:一对多
1个用户可以有多个订单,多个订单属于1个用户
人和身份证号:一对一
1个人有1个身份证号,1个身份证号对应1个人
老师和学生:多对多
1个学生有多个老师,1个老师有多个学生
在多对一中,把一个订单单独拿出来,它都只属于1个用户,所以 MyBatis 把多对一看作一对一。
一对一
演示例子:
Account 账号类
public class Account {
private Integer id; //账号id
private Integer uid; //账号的主人的id
private Double money;
//省略 getter 、setter、toString 方法
}
AccountUser 类
public class AccountUser extends Account {
private String username;
private String address;
//省略 getter 、setter
//这里的toString 方法为了观看效果,把父类(即 Account 类的 toString 方法也用了)
@Override
public String toString() {
return super.toString()+"-----AccountUser{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
'}';
}
}
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="com.rgb3.dao.IAccountDao">
<!--查询所有账号信息和这些账号对应的人名和地址-->
<select id="findAccountUsers" resultType="com.rgb3.domain.AccountUser">
select account.*,user.username,user.address from t_account account , t_user user where account.uid = user.id;
</select>
</mapper>
数据表 t_account
数据表t_user
测试方法
//查询所有账号信息和这些账号对应的人名和地址
@Test
public void findAccountUsers(){
List<AccountUser> all = accountDao.findAccountUsers();
for (AccountUser accountUser:all){
System.out.println(accountUser);
}
}
提示:这种创建一个类继承另一个类来达到查询效果的方法实际中不常用,下面是另一种方法。
在Account 类里添加 User 类的对象引用
public class Account {
private Integer id;
private Integer uid;
private Double money;
//从表包含主表的一个实体对象引用
private User user;
//省略 setter、getter、toString 方法
}
IAccountDao 接口添加一个方法演示
//查询所有账号信息和这些账号对应的人名和地址
List<Account> findAccountUsers2();
IAccountDao.xml
添加定义封装 Account 和 User 的ResultMap
<!--定义封装 Account 和 User 的ResultMap-->
<resultMap id="accountUserMap" type="Account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 一对一关系映射,封装 user 的内容 -->
<association property="user" column="uid" javaType="user">
<id property="userId" column="id"></id>
<result property="userName" column="username"></result>
<result property="userBirthday" column="birthday"></result>
<result property="userAddress" column="address"></result>
</association>
</resultMap>
<!--查询所有账号信息和这些账号对应的人名和地址-->
<select id="findAccountUsers2" resultMap="accountUserMap">
select user.*,account.id as aid,account.uid,account.money from t_account account , t_user user where account.uid = user.id;
</select>
<association>里的 column 值表示用哪个字段获取 user,javaType 值表示User类,因为之前有起别名所以不用写全限定类名了,直接写了user
测试方法
//查询所有账号信息和这些账号对应的人名和地址
@Test
public void findAccountUsers2(){
List<Account> all = accountDao.findAccountUsers2();
for (Account accountUser:all){
System.out.print(accountUser+" ");
System.out.println(accountUser.getUser());
}
}
一对多
一个人有多个账号
User 类
public class User implements Serializable {
private int userId;
private String userName;
private Date userBirthday;
private String userAddress;
//一对多映射,主表实体应该包含从表实体的集合引用
private List<Account> accounts;
//省略 setter、getter、 toString方法
}
Account 类
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
//省略 setter、getter、 toString方法
}
IUserDao 接口 添加1个演示方法
//一对多查询所有
List<User> findUserAccountAll();
SQL 语句
SELECT u.*,account.id as aid,account.uid,account.money FROM t_user u LEFT OUTER JOIN t_account account ON account.uid=u.id
在数据库里查询是这样的:
IUserDao.xml
<!--一对多结果映射-->
<resultMap id="userAccountMap" type="user">
<id property="userId" column="id"></id>
<result property="userName" column="username"></result>
<result property="userBirthday" column="birthday"></result>
<result property="userAddress" column="address"></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="findUserAccountAll" resultMap="userAccountMap">
<!--select * from t_user;-->
SELECT u.*,account.id as aid,account.uid,account.money FROM t_user u LEFT OUTER JOIN t_account account ON account.uid=u.id
</select>
<collection> 里的 ofType 属性指集合里元素的类型,所以这里是Account 类。
这里因为 t_user 表里有个 id 字段,t_account 表里也有个 id 字段,重复会不行,所以把 t_account 表里的 id 字段起了个别名 aid。
t_user 表
t_account 表
测试方法
//一对多查询全部
@Test
public void findUserAccountAll(){
List<User> all = userDao.findUserAccountAll();
for (User user : all){
System.out.println(user);
System.out.println(user.getAccounts());
System.out.println("------------------------");
}
}
多对多
1个人可以有多个角色(如学生、儿子、工人),1种角色也可以赋予多个人。
演示查询角色获取角色所属的个人信息
创建角色表 t_role
角色和用户的中间表 t_user_role,注意这里使用了2个外键
CREATE TABLE `t_user_role`(
`uid` INT(10) NOT NULL,
`rid` INT (10) NOT NULL,
PRIMARY KEY (`uid`,`rid`),
CONSTRAINT `FK_rid` FOREIGN KEY (`rid`) REFERENCES `t_role` (`id`),
CONSTRAINT `FK_uid` FOREIGN KEY (`uid`) REFERENCES `t_user` (`id`)
)ENGINE= INNODB DEFAULT CHARSET=utf8;
用户表 t_user 就还是用之前的
创建 Role 角色类
public class Role implements Serializable {
private Integer roleId;
private String roleName;
private String roleDesc;
//多对多映射,1个角色可以赋予多个人
private List<User> users;
//省略getter、setter、toString 方法
}
IRoleDao 接口
public interface IRoleDao {
//查询所有
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="com.rgb3.dao.IRoleDao">
<resultMap id="roleMap" type="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="userId" column="id"></id>
<result property="userName" column="username"></result>
<result property="userBirthday" column="birthday"></result>
<result property="userAddress" column="address"></result>
</collection>
</resultMap>
<!--多对多查询-->
<select id="findAll" resultMap="roleMap">
SELECT u.*,role.id AS rid,role_name,role_desc FROM t_role role
LEFT OUTER JOIN t_user_role user_role ON role.id=user_role.rid
LEFT OUTER JOIN t_user u ON u.id=user_role.uid
</select>
</mapper>
提示:left 前面最好加空格,这样和上一句拼接起来就不会黏在一起。
RoleTest测试类
public class RoleTest {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession session;
private IRoleDao roleDao;
@Before//测试方式运行前运行
public void init() throws Exception {
//读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂
factory = new SqlSessionFactoryBuilder().build(in);
//用工厂生产SqlSession对象
session = factory.openSession();
//用SqlSession创建Dao接口的代理对象
roleDao = session.getMapper(IRoleDao.class);
}
@After//测试方法运行后运行
public void destroy() throws Exception {
//提交事务
session.commit(); //保存、修改、删除操作时,如果你没这步提交事务,数据是不能成功写入数据库的
in.close();
session.close();
}
//多对多查询
@Test
public void findAll(){
List<Role> all = roleDao.findAll();
for (Role role:all){
System.out.println(role);
System.out.println(role.getUsers());
}
}
}
SQL 语句在数据库里查询是这样的:
启动测试方法:
成功。
演示查询用户获取用户所拥有的角色,即1个用户可以有多种角色,这与上面的演示例子相似。
User 类
public class User implements Serializable {
private int userId;
private String userName;
private Date userBirthday;
private String userAddress;
//多对多映射,1个用户可以有多种角色
private List<Role> roles;
//省略getter、setter、toString 方法
}
IUserDao 接口添加演示方法
//多对多查询,1个人有多种角色
List<User> findUserRolesAll();
IUserDao.xml
<!--多对多结果映射,1个用户有多种角色-->
<resultMap id="userRoleMap" type="user">
<id property="userId" column="id"></id>
<result property="userName" column="username"></result>
<result property="userBirthday" column="birthday"></result>
<result property="userAddress" column="address"></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>
<!--多对多映射查询,1个用户可以有多种角色-->
<select id="findUserRolesAll" resultMap="userRoleMap">
<!--select * from t_user;-->
SELECT u.*,role.id AS rid,role_name,role_desc FROM t_user u
LEFT OUTER JOIN t_user_role user_role ON u.id=user_role.uid
LEFT OUTER JOIN t_role role ON role.id=user_role.rid
</select>
SQL 语句在数据库查询是这样的:
UserAccountTest 测试类添加测试方法
//多对多查询,1个人有多种角色
@Test
public void findUserRolesAll(){
List<User> all = userDao.findUserRolesAll();
for (User user : all){
System.out.println(user);
System.out.println(user.getRoles());
System.out.println("------------------------");
}
}
完成。