《Mybatis入门到精通》——3.动态SQL&多表映射

Day_03

连接池、动态SQL;

连接池

(1)连接池:

在实际开发中都会使用连接池,因为它可以减少我们获取连接所消耗的时间;

(2)mybatis中的连接池:

mybatis连接池提供了3种配置方式;

配置的位置:主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式。

type属性的值,有3种:

  • POOLED:采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
  • UNPOOLED:采用传统的获取连接的方式,虽然也实现javax.sql.DataSource接口,但是并没有使用池的思想
  • JNDI:采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样的

注意:如果不是web或者maven的war工程,是不能使用的;学习时使用的是tomcat服务器,采用的连接池就是dbcp连接池;

Mybatis事务

什么是事务?

  • 事务的四大特性ACID;
  • 不考虑隔离性会产生的3个问题;
  • 解决办法:四种隔离级别;

它是通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚,默认update和delete是不自动提交的,需要手动commit;

动态SQL

(1) if 标签

<!-- 根据条件判断user的username是否为空,来动态的增加SQL条件语句 -->
<select id="findByCondition" resultType="com.ming.domain.User" parameterType="com.ming.domain.User">
    <!-- 这里有个where1=1
    select * from user where 1=1
    <if test="username!=null">
        and username=#{username}
    </if>
    <if test="sex!=null">
        and sex=#{sex}
    </if>
    -->
    <!-- 写到<where>里面 不需要 where1=1 -->
    select * from user
    <where>
        <if test="username!=null">
            and username=#{username}
        </if>
        <if test="sex!=null">
            and sex=#{sex}
        </if>
    </where>
</select>

测试:

@Test
public void findByCondition() {
    User u = new User();
    User u0 = new User();
    User u1 = new User();
    //username为空,会查询所有
    List<User> users0 = userDao.findByCondition(u0);
    users0.stream().forEach(System.out::println);

    //username不为空,会查询username=#{username}
    u.setUsername("老王");
    List<User> users = userDao.findByCondition(u);
    users.stream().forEach(System.out::println);

    //username和sex不为空,会查询username=#{username} sex=#{sex}
    u1.setUsername("老王");
    u1.setSex("女");
    List<User> users1 = userDao.findByCondition(u1);
    users1.stream().forEach(System.out::println);

    session.commit();
}

(2) where和foreach标签
用于多个查询的sql:select * from user where id in(41, 42, 45);
通过一个类中传入集合的方法;

定义类:

/**
 * @author A
 */
public class QueryVo {
    //为测试OGNL #{user.username}
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    private User user;

    //为测试动态SQL if标签

    private List<Integer> ids;

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }

}

定义接口方法

/**
 * 根据QueryVo 中提供的id集合,查询用户信息 where id in (...)
 * @param vo
 * @return
 */
List<User> findByIdGroup(QueryVo vo);

定义mapper.xml的SQL

<!-- 条件查询的sql select * from user where id in(41,42,46) 条件:满足id在(...)中 通过一个 QueryVo类 传入集合的方法 -->
<select id="findByIdGroup" resultType="com.ming.domain.User" parameterType="com.ming.domain.QueryVo">
    select * from user
    <!--<include refid="selectFrUser"></include>-->

    <where>
        <if test="ids != null and ids.size()>0">
            <!-- 这里有5个标签 collections open close item separator 注意 item要和下面的#{user.id}相对应 -->
            <foreach collection="ids" open="and id in (" close=")" item="user.id" separator=",">
                #{user.id}
            </foreach>
        </if>
    </where>

</select>

测试:

@Test
public void findByIds(){
    QueryVo queryVo = new QueryVo();
    List<Integer> list = new ArrayList<Integer>();
    list.add(41);
    list.add(42);
    list.add(46);
    queryVo.setIds(list);
    List<User> users = userDao.findByIdGroup(queryVo);
    users.stream().forEach(System.out::println);
    //session.commit();
}

(3) 复用SQL语句

对于经常重复使用的SQL语句,如SELECT * FROM user,很多<select>...里面都会用到,这句话经常用,可以作为一个<sql>标签,作为引用导入,有点像JSP/HTML;

<sql id="selectFrUser">
    select * from user
</sql>

<!-- 条件查询的sql select * from user where id in(41,42,46) 条件:满足id在(...)中 通过一个 QueryVo类 传入集合的方法 -->
<select id="findByIdGroup" resultType="com.ming.domain.User" parameterType="com.ming.domain.QueryVo">
    <!--下面这句话经常用 可以作为一个<sql>标签 作为引用导入 有点像JSP/HTML-->
    -- select * from user
    <include refid="selectFrUser"></include>

    <where>
        <if test="ids != null and ids.size()>0">
            <!-- 这里有5个标签 collections open close item separator 注意 item要和下面的#{user.id}相对应 -->
            <foreach collection="ids" open="and id in (" close=")" item="user.id" separator=",">
                #{user.id}
            </foreach>
        </if>
    </where>

</select>

注意: 尽量不要使用分号,sql语句可能会拼接

(以上内容的测试项目为 mybatis_01)

Day_04

多表查询(一对一、一对多、多对多)


为防止对之前代码的影响(除了测试Account还增加了role角色表),新建了UserCopy类以及对应的xml、user_copy表;

示例:用户和账户

  • 一个用户可以有多个账户
  • 一个账户只能属于一个用户(多个账户也可以属于同一个用户)

步骤:
1、建立两张表:用户表 帐户表
让用户表和账户表之前具备一对多的关系:需要使用外键在帐户表上添加
2、建立两个实体类:用户实体类和账户实体类,让用户和账户的实体类能体现出来一对多的关系
3、配置两个配置文件
  用户的配置文件
  账户的配置文件
4、实现配置:
  当我们查询用户的时候,可以同时得到用户下所包含的信息
  当我们查询账户时,我们可以同时得到账户所属的用户信息

1. 一对一/一对多 映射

一张用户表,一张账户表;
一个用户下可能有多个账户,一个账户只对应一个用户;

表结构:

uid表示用户的id;

(1) 从账户表查询,返回每个账户信息+每个账户对应的用户信息,即一对一查询;
Account表的实体类应该包含一个主表实体的对象引用user;

/**
 * @author A
 */
public class Account implements Serializable {

    //多封装一个User属性,查询的时候 从Acount关联对应的User信息
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    private Integer id;
    private Integer uid;
    private Double money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                " id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }
}

Account的DAO接口IAccountDao

public interface IAccountDao {

    /**
     * 查询所有账户
     * @return
     */
    List<Account> findAll();

    /**
     * 查询所有账户+对应的用户的username和地址
     * @return
     */
    List<AccountUser> findAcountUser();

    /**
     * 查询所有账户+对应的用户的username和地址,封装了User对象
     * @return
     */
    List<Account> findAcountUser1();
}

IAccountDao的Mapper .xml

注意:
(1) 一对一的映射,包含一个user对象,ResultMap里面加一栏<association>...
(2) <association>栏里面的javaType一定要指名user的实体类名,column中指名从表的外键;

<!--一对一的关系映射,配置封装user的内容 column中指名从表的外键 property="user"指的是单个实体类的引用-->
<association property="user" column="uid" javaType="com.ming.domain.User">

测试:

@Test
public void testSelectAll2() {
    List<Account> accounts = accountDao.findAcountUser1();
    accounts.stream().forEach((a) -> {
        System.out.println(a);
        System.out.println(a.getUser());
    });
}

(2) 从user_copy表去查询所有的user,包含每个user的信息+每个user的账户(可以是多个,所以定义成一个List<Account>),即一对多的映射;

定义user表的实体类,主表实体中应该包含从表实体的集合引用List<Account>

/**
 * @author A
 * User表的实体类;
 */
public class User implements Serializable {


    //一个user有多个ACCOUNT,建立 一对多的关系, 多封装一个List<Account>属性,查询的时候 从User关联对应的多个Acount信息

    private List<Account> accounts;
    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    ...

User的DAO接口

区别于之前的<association>栏,这里使用<collection>标签,对应的List<Account>集合;

<!--一对多的关系映射,配置封装List<Account>的内容(一对多会自动封装到List<Account>里面) column中指名从表的外键(这里User表没有外键) property="accounts"指的是单个实体类的引用 javaType指account的类型(不是accounts的类型List)-->
<collection property="accounts"  ofType="com.ming.domain.Account">
    <id property="id" column="aid"/><!-- 这里property指实体类Account的属性名 column因为id与User里面的id重复,所以改名aid -->

测试

@Test
public void testSelectAll2() {
    List<User> users = userDao.findAcountUser1();
    users.stream().forEach((u) -> {
        System.out.println(u);
        System.out.println(u.getAccounts());
        System.out.println();
    });
}

2. 多对多映射

一个用户有多个身份;
一个身份个能多个用户都拥有;

步骤:

1、建立两张表:用户表 用户表
让用户和角色表具有多对多的关系。需要使用中间表,中间表中包含各自的主键,在中间表中是外键。

2、建立两个实体类:用户实体类和角色实体类
让用户和角色的实体类能体现出来多对多的关系
各自包含对方一个集合引用

3、配置两个配置文件
用户的配置文件
角色的配置文件

4、实现配置:
当我们查询用户的时候,可以同时得到用户下所包含角色的信息
当我们查询角色时,我们可以同时得到角色所赋予的用户信息

表结构:
user_copy与之前的user表一样;

角色表role

用户和角色的关联表user_role

(1) 从role查user,多对多
定义role表的实体类,一个实体表中包含另一个实体表的集合引用List<User>

public class Role implements Serializable {

    private Integer roleId;
    private String roleName;
    private String roleDesc;

    /**
     * 多对多的关系映射:一个角色可以赋予多个用户
     */
    private List<UserCopy> users;

    public List<UserCopy> getUsers() {
        return users;
    }

    public void setUsers(List<UserCopy> users) {
        this.users = users;
    }
    ...

role的DAO接口

public interface IRoleDao {
    /**
     * 查询所有角色
     * @return
     */
    List<Role> findAll();

    /**
     * 多表查询 经过中间表 查询所有角色
     * @return
     */
    List<Role> findAll1();

IRoleDao的XML配置

与前面一样,因为持有一个UserCopy的集合List<UerCopy>,使用<collection>标签

测试

@Test
public void testSelectAll1() {
    List<Role> roles= roleDao.findAll1();
    roles.stream().forEach((r)->{
        System.out.println(r);
        System.out.println(r.getUsers());
        System.out.println();
    });
}

(2) 从user_copy查role,多对多
与Role;类似,定义user_copy表的实体类,包含另一个实体表的集合引用List<Role>

public class UserCopy implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    /**
     * 多对多的关系映射:一个用户可以对应多个角色
     */
    private List<Role> roles;

Dao接口

/**
 * @author A
 * User的持久层接口
 */
public interface IUserCopyDao {
    /**
     * 查询所有UserCopy表的记录-操作
     * @return
     */
    List<UserCopy> findAll();

    /**
     * 关联查询 查询所有UserCopy表的记录-操作
     * @return
     */
    List<UserCopy> findAll1();
}

IUserCopyDao的XML配置

测试

@Test
public void testSelectAll1() {
    List<UserCopy> users= userCopyDao.findAll1();
    users.stream().forEach((r)->{
        System.out.println(r);
        System.out.println(r.getRolse());
        System.out.println();
    });
}

3. JNDI

JNDI是Java命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一。

4. 补充: MySQL的多表查询

可以在 SELECT, UPDATE 和 DELETE 语句中使用 Mysql 的 JOIN 来联合多表查询。

JOIN 按照功能大致分为如下三类:

  • INNER JOIN(内连接,或等值连接):获取两个表中字段匹配关系的记录。
  • LEFT JOIN(左连接):获取左表所有记录,即使右表没有对应匹配的记录。
  • RIGHT JOIN(右连接): 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录。

(1) 表结构测试

(2) 内连接
接下来我们就使用MySQL的INNER JOIN(也可以省略 INNER 使用 JOIN,效果一样)来连接以上两张表来读取runoob_tbl表中所有runoob_author字段在tcount_tbl表对应的runoob_count字段值;

(3) 左/右连接

  • MySQL LEFT JOIN

MySQL left join与join有所不同,会读取左边数据表的全部数据,即便右边表无对应数据。

  • MySQL RIGHT JOIN

与左连接相似,MySQL RIGHT JOIN会读取右边数据表的全部数据,即便左边边表无对应数据。

(4) 其他:全连接、左/右独有;

全连接:

两表关联,查询左表独有的数据:

两表关联,查询右表独有的数据:

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值