Mybatis学习之路 第三篇

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条件语句
  1. 在IUserDao中添加如下查询接口代码:
/**
 * 根据传入的参数条件查询
 *查询条件:有可能有用户名,性别等,有可能都有
 */
List<User> findUserByCondition(User user);
  1. 在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>
  1. 在测试类中测试:
@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 的条件拼装,我们可以采用标签来简化开发。
  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);
  1. 在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;
        }
    }
  2. 在IUserDao中添加如下:
    /**
     * 根据QueryVo中提供的id集合,查询用户信息
     * @return
     */
    List<User> findUserInIds(QueryVo vo);
    
  3. 在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>
  4. 在测试类中测试:
    @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语句代码,我们就可以使用抽取代码方法了。
  1. 原来的代码块如下:
    <!-- 配置查询所有-->
    <select id="findAll" resultType="User">
        select * from user
    </select>
  2. 使用代码抽取后如下,在需要的地方直接引用就可以啦
    <!--抽取重复的代码-->
    <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 一对一查询(多对一)
  • 需求:查询所有账户信息,关联查询下单用户信息。
  • 注意:因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询。如果从用户信息出发查询用户下的账户信息则为一对多查询,因为一个用户可以有多个账户。
  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);
  1. 编写Account类如下,get、set和toString方法省略了,自行添加:
    public class Account implements Serializable {
        private Integer id;
        private Integer uid;
        private Double money;
    }

4.1.1 通过继承Account子类的方式实现:
  1. 编写继承Account类的AccountUser类,省略get、set和toString方法,toString方法中加上super.toString():
    public class AccountUser extends Account {
        private String username;
        private String address;
    }
  2. 编写IAccountDao接口方法:
    public interface IAccountDao {
        /**
         * 查询所有账户,同时获取账户的所属用户信息
         * @return
         */
        List<Account> findAll();
    }
  3. 编写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>
  4. 编写AccountTest测试类,运行即可:
    @Test
    public void testFindAllAccountUser() {
        List<AccountUser> aus = accountDao.findAllAccount();
        for(AccountUser au: aus) {
            System.out.println(au);
        }
    }
4.1.2 使用resultMap,专门定义的resultMap用于映射一对一查询结果
  • 从表实体应该包含一个主表实体的对象引用
  1. 修改Account类,省略了get、set和toString方法:
    public class Account implements Serializable {
        private Integer id;
        private Integer uid;
        private Double money;
    
        //从表实体应该包含一个主表实体的对象引用
        private User user;
    }
  2. 增加IAccountDao中的方法:
    /**
     * 查询所有账户,同时还要获取到当前账户的所属用户信息
     * @return
     */
    List<Account> findAll();
  3. 增加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>
  4. 编写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 一对多查询(多对一)
  • 一个用户可能有多个账号,列出所有用户,以及用户关联的账户。(和用户有关联的账户才会被列出)
  1. 修改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;
    }

  1. 添加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>

  1. 在IUserDao接口中添加如下代码:
    /**
     * 查询所有用户
     * @return
     */
    List<User> findAll();

  1. 在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多对多
  • 有三张表,分别是用户表,角色表和用户角色(中间表)。
  1. 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);

  1. 编写Role类。注意这里用的属性名称和数据库不一样哦。同样省略get、set和toString方法:

    public class Role implements Serializable {
        private Integer roleId;
        private String roleName;
        private String roleDesc;
    
        //多对多的关系映射:一个角色可以赋予多个用户
        private List<User> users;
    }
  2. 编写IRoleDao接口如下:

    public interface IRoleDao {
        /**
         * 查询所有角色
         * @return
         */
        List<Role> findAll();
    }
  3. 编写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>
  4. 编写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的多对多是一样的方式,这里不再列举啦。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值