Mybatis框架学习笔记(02)

目录

01Mybatis的连接池技术

  • 在 Mybatis 的主配置文件中( SqlMapConfig.xml ), 通过<dataSource type=”pooled”>来实现 Mybatis 中连接池的配置。
  • 通过type属性来指定使用哪种数据源
  • mybatis的数据源分为三类:
UNPOOLED:不使用连接池的数据源
POOLED :使用连接池的数据源
JNDI :使用 JNDI 实现的数据源
  • 在这三种数据源中,我们一般采用的是 POOLED 数据源(很多时候我们所说的数据源就是为了更好的管理数据库连接,也就是我们所说的连接池技术)

1.1mybatis中的连接池配置

  • 连接池配置就是在主配置文件(SqlMapConfig.xml )中, 具体配置如下:
<!-- 配置数据源(连接池)信息 -->
<dataSource type="POOLED">
	<property name="driver" value="${driver}"/>
	<property name="url" value="${url}"/>
	<property name="username" value="${username}"/>
	<property name="password" value="${password}"/>
</dataSource>
  • MyBatis 在初始化时, 根据<dataSource>的 type 属性来创建相应类型的的数据源 DataSource,即:
    type=”POOLED”: 采用传统的javax.sql.DataSource规范中的连接池,使用完不关闭,归还到连接池中,mybatis中有针对规范的实现
    type=”UNPOOLED” : 采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池的思想,随时使用随时创建,使用完就关闭
    type=”JNDI”: MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用
  • 数据库连接是我们最为宝贵的资源,只有在要用到的时候,才去获取并打开连接,当我们用完了就再立即将数据库连接归还到连接池中

02Mybatis 的事务控制

  • 在 JDBC 中我们可以通过手动方式将事务的提交改为手动方式,即通过setAutoCommit()方法就可以调整,参数为True则为自动提交,参数为False则为手动提交。
  • Mybatis 框架是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC 的setAutoCommit()方法来设置事务提交方式的

JDBC事务

  • 1.在JDBC中处理事务,都是通过Connection完成的。同一事务中所有的操作,都在使用同一个Connection对象。JDBC事务默认是开启的,并且是默认提交。
  • 2.JDBC Connection 接口提供了两种事务模式:自动提交和手工提交
  • 3.JDBC中的事务java.sql.Connection 的三个方法与事务有关:
    setAutoCommit(boolean):设置是否为自动提交事务,每条执行的SQL语句都是一个单独的事务,如果设置为false,需要手动提交事务。
    commit():提交结束事务。
    rollback():回滚结束事务。

Mybatis中的事务是通过SqlSession对象的commit方法和rollback方法实现事务的提交和回滚

2.1什么是事务?

事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功

2.2为什么要使用事务?

  • 事务是为解决数据安全操作提出的,
    例如:银行转帐业务,账户A向账户B转1元钱,A账户首先要减去1元钱,然后B账户再增加1元。假如在中间网络出现了问题,A账户减去1元已经结束,B因为网络中断而操作失败,那么整个业务失败,必须做出控制,要求A账户转帐业务撤销。这才能保证业务的正确性,完成这个操走就需要事务,将A账户资金减少和B账户资金增加放到同一个事务里,要么全部执行成功,要么全部撤销,这样就保证了数据的安全性

2.3事务的四个特性(ACID)

  1. 原子性(atomicity):事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行。
  2. 一致性(consistency):事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。(实例:转账,两个账户余额相加,值不变。)
  3. 隔离性(isolation):一个事务的执行不能被其他事务所影响。
  4. 持久性(durability):一个事务一旦提交,事物的操作便永久性的保存在DB中。即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

2.4Mybatis中事务提交方式

  • mybatis中更改事务提交方式使用public SqlSession openSession(boolean autoCommit),默认为自动提交方式,设置为false则为手动提交
  • 为了复习,再把流程走一遍
  • 只做一个根据QueryVo条件进行查询的操作
    在这里插入图片描述

2.4.1创建一个maven工程(名字为mybatis)

2.4.2创建数据库和表

1.创建数据库语句(myMybatis)
create database myMybatis;

2.创建表语句
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;

3.在表中添加几条记录
insert  into `user`(`id`,`username`,`birthday`,`sex`,`address`) values 
(41,'老王','2018-02-27 17:47:08','男','北京'),
(42,'小王','2018-03-02 15:09:37','女','北京金燕龙'),
(43,'隔壁老王','2018-03-04 11:34:34','女','北京金燕龙'),
(45,'隔壁小王','2018-03-04 12:04:06','男','北京金燕龙'),
(46,'老王','2018-03-07 17:37:26','男','北京'),
(48,'老张','2018-03-08 11:44:00','女','北京修正');

2.4.3在包中创建dao接口(IUserDao)

/*持久层接口*/
public interface IUserDao(){
	/* 根据queryVo中的条件查询用户*/
    List<User> findUserByVo(QueryVo vo);
}

2.4.4在包domain中创建User.java和QueryVo.java

  • QueryVo.java
public class QueryVo(){
	private User user;
	public void setUser(User user){
		this.user=user;
		}
	public User getUser(){
		return user;
	}
}
  • User.java
public class User(){
	//定义私有属性
	private Integer userId;
	private String userName;
	private String userAddress;
	private String userSex;
	private Date userBirthday;
	
	public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserAddress() {
        return userAddress;
    }
    public void setUserAddress(String userAddress) {
        this.userAddress = userAddress;
    }

    public String getUserSex() {
        return userSex;
    }
    public void setUserSex(String userSex) {
        this.userSex = userSex;
    }

    public Date getUserBirthday() {
        return userBirthday;
    }
    public void setUserBirthday(Date userBirthday) {
        this.userBirthday = userBirthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", userAddress='" + userAddress + '\'' +
                ", userSex='" + userSex + '\'' +
                ", userBirthday=" + userBirthday +
                '}';
    }

}

2.4.5在Resource文件夹下创建数据库链接信息配置文件(db.properties)

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/myMybatis
jdbc.user=root
jdbc.password=12345678

2.4.6在Resource文件夹下创建主配置文件(SqlMapConfig.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 配置properties,读取数据库链接信息-->
    <properties resource="db.properties"></properties>
    
    <!--使用typeAliases配置别名,它只能配置domain中类的别名 -->
    <!--配置过之后就可以直接使用类名了,不区分大小写-->
    <typeAliases>
        <package name="com.aismall.domain"></package>
    </typeAliases>

    <!--配置环境-->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务:类型为jdbc类型 -->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置连接池-->
            <!--只有在通过properties标签读取数据库链接信息后才能这样写-->
            <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>
    
    <!-- 配置映射文件的位置 -->
    <!--注意此处使用的是package标签-->
    <mappers>
        <package name="com.aismall.dao"></package>
    </mappers>
</configuration>

2.4.7在Resource文件夹下添加配置(log4j.properties)

# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE            debug   info   warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE

# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

2.4.8在Resource文件加下创建与dao接口相同的包,然后在包中添加IUserDao.xml文件(映射配置文件)

  • 我们只做根据queryVo的条件查询用户
<?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.aismall.dao.IUserDao">
    <!-- 配置 查询结果的列名和实体类的属性名的对应关系 -->
    <resultMap id="userMap" type="uSeR">
        <!-- 主键字段的对应 -->
        <id property="userId" column="id"></id>
        <!--非主键字段的对应-->
        <result property="userName" column="username"></result>
        <result property="userAddress" column="address"></result>
        <result property="userSex" column="sex"></result>
        <result property="userBirthday" column="birthday"></result>
    </resultMap>
    
    <!-- 根据queryVo的条件查询用户 -->
    <select id="findUserByVo" parameterType="QueryVo" resultMap="userMap">
        select * from user where username like #{user.userName}
    </select>
</mapper>

2.4.9在包中编写测试类(mybatisTest.java)

public class mybatisTest {

    private InputStream in;
    private SqlSession sqlSession;
    private IUserDao userDao;

    @Before//用于在测试方法执行之前执行
    public void init()throws Exception{
        //1.读取配置文件,生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.获取SqlSession对象
        sqlSession = factory.openSession(true);
        //4.获取dao的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After//用于在测试方法执行之后执行
    public void destroy()throws Exception{
        //提交事务
       // sqlSession.commit();
        //6.释放资源
        sqlSession.close();
        in.close();
    }

    /**
     * 测试使用QueryVo作为查询条件
     */
    @Test
    public void testFindByVo(){
        QueryVo vo = new QueryVo();
        User user = new User();
        user.setUserName("%王%");
        vo.setUser(user);
        //5.执行查询一个方法
        List<User> users = userDao.findUserByVo(vo);
        for(User u : users){
            System.out.println(u);
        }
    }
}

注意

  • 前面铺垫了那么多就为了设置事务的提交方式,在第三步,如果openSession方法中不传参数或者参数为true,则为自动提交方式,在destory方法中就不再需要手动提交事务(调用commit方法),如果参数为false,则需要手动提交参数(调用commit方法)

03mybatis的动态SQL

  • 除了之前我们使用的简单的SQL 查询之外,有些时候业务逻辑复杂时,我们的 SQL 是动态变化的,这时候简单的SQL查询将不能那么足要求,我们就需要使用下面介绍的方法来进行SQL语句的动态设置
  • 我们在刚才创建的项目上进行改写

3.1动态 SQL 之 if 标签

  • 需求:动态查询名称为老王并且性别为男的用户

  • 在IUserDao接口中添加方法

	/*根据传入的参数条件进行查询
    * user为查询的条件,可能有用户名,性别,地址,也可能没有
    * */
    List<User> findUserByCondition(User user);
  • 在IUSerDao.xml映射配置文件中添加配置
<select id="findUserByCondition" resultMap="userMap" parameterType="user">
	select * from user where 1=1
	 /*两个判断条件*/
	<if test="userName!=null">
		username=#{userName}
	</if>
	<if test="userSex!=null">
		and sex=#{userSex}
	</if>
</select>
注意: 
<if>标签的 test 属性中写的是对象的属性名,
如果是包装类的对象要使用 OGNL 表达式的写法。

 where 1=1的作用就是在SQL动态查询中,无论出现什么因素,
 都能保证有一条语句能正确执行(也就是防止where子句中内容为空时出现报错)
  • 在测试类中添加
@Test
    public void testFindUserByCondition(){
        User user=new User();
        user.setUserName("老王");
        user.setUserSex("男");
        List<User> users=userDao.findUserByCondition(user);
        for (User u:users) {
            System.out.println(u);
        }
    }

3.2动态 SQL 之where标签

  • 刚才使用了if标签进行数据的查询,如果不使用where 1=1,当出现所有设定的判断语句都不成立时,就会因为where子句为空而出错
  • 我们可以把上面的查询语句进行改写,使用where标签,这样就不会出现上面的问题了,改写之后的映射配置如下:
 <!--根据条件查询-->
    <select id="findUserByCondition" resultMap="userMap" parameterType="user">
        select * from user
        /*两个判断条件都放在where标签中*/
        <where>
            <if test="userName!=null">
                and username=#{userName}
            </if>
            <if test="userSex!=null">
                and sex=#{userSex}
            </if> 
        </where>
    </select>

3.3动态标签之foreach标签

  • 需求:传入多个 id 查询用户信息,进行范围查询
  • 进行范围查询时,就要将一个集合中的值,作为参数动态添加进来
  • 对应的sql 语句可以是以下两种:
SELECT * FROM USERS WHERE (id =10 OR id =89 OR id=16)
SELECT * FROM USERS WHERE  id IN (10,89,16)
  • 我们继续在上面的代码中进行改写
  • 在QueryVo.java 中添加一个ids属性,并生成该属性的set和get方法
private List<Integer> ids;
  • 在IUserDao接口中添加方法
/*根据QueryVo中提供的id集合,查询用户信息*/
    List<User> findUserByIds(QueryVo vo);
  • 在映射配置文件IUserDao.xml中进行配置(重点)
<!--根据QueryVo中提供的id集合,查询用户信息-->
    <select id="findUserByIds" parameterType="QueryVo" resultMap="userMap">
        select * from user
        <where>
            <if test="ids!=null and ids.size>0">
                <foreach collection="ids" open="id in (" close=")"  item="id" separator=",">
                    #{id}
                </foreach>
            </if>
        </where>
    </select>
  • 分析映射配置:
    <foreach>标签用于遍历集合,它的属性:
    collection:代表要遍历的集合元素,注意编写时不要写#{}
    open:代表语句的开始部分
    close:代表结束部分
    item:代表遍历集合的每个元素,生成的变量名
    sperator:代表分隔符
    遍历之后结果如下:
select * from user where id in (id1,id2,di3,,,,)
  • 在测试类中添加
@Test
    public void testFindUserByIds(){
        QueryVo vo=new QueryVo();
        List<Integer> list=new ArrayList<Integer>();
        list.add(41);
        list.add(42);
        list.add(43);
        vo.setIds(list);
        List<User> users=userDao.findUserByIds(vo);
        for (User u:users) {
            System.out.println(u);
        }
    }

3.4相同SQL代码的抽取

  • Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
  • sql标签抽取重复代码
  • include标签引入抽取的代码
	<!--抽取重复Sql代码-->
    <sql id="defaultSql">
        select * from user
    </sql>
    <!--根据QueryVo中提供的id集合,查询用户信息-->
    <select id="findUserByIds" parameterType="QueryVo" resultMap="userMap">
        /*引入抽取的代码*/
        <include refid="defaultSql"></include>
        <where>
            <if test="ids!=null and ids.size>0">
                <foreach collection="ids" open="id in (" close=")"  item="id" separator=",">
                    #{id}
                </foreach>
            </if>
        </where>
    </select>

04mybatis多表查询之一对多

  • 表之间的关系有
一对多
多对一
一对一
多对多

演示一对多的示例:用户和账户

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

步骤:

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

4.1环境搭建

  • 目录结构
    在这里插入图片描述

4.1.1新建项目(mybatis)

  • 引入坐标
 <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
    </dependencies>

4.1.2在myMybatis数据库中添加account表

1.创建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;

2.添加数据
insert  into `account`(`ID`,`UID`,`MONEY`) values 
(1,46,1000),
(2,45,1000),
(3,46,2000);

4.1.3在包中创建实体类(User和Account)

  • User.java中的属性
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
  • Account.java中的属性
 private Integer id;
 private Integer uid;
 private Double money;

4.1.4在包中创建接口(IUserDao和IAccountDao)

  • IUserDao接口添加方法
 /*查询所有用户*/
    List<User> findAll();
  • IAccountDao接口添加方法
  /*查询所有账户*/
    List<Account> findAll();

4.1.5在Resource文件夹下导入配置文件

  • SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 配置properties-->
    <properties resource="db.properties"></properties>

    <!--使用typeAliases配置别名,它只能配置domain中类的别名 -->
    <typeAliases>
        <package name="com.aismall.domain"></package>
    </typeAliases>

    <!--配置环境-->
    <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>
    <!-- 配置映射文件的位置 -->
    <mappers>
        <package name="com.aismall.dao"></package>
    </mappers>
</configuration>
  • db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/myMybatis
jdbc.username=root
jdbc.password=12345678
  • log4j.properties
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE            debug   info   warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE

# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

4.1.6在包下添加映射配置文件(IUserDao.xml和IAccountDao.xml)

  • IUserDao.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.aismall.dao.IUserDao">
    <!-- 查询所有 -->
    <select id="findAll" resultType="User" >
        select * from user;
    </select>
</mapper>
  • 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.aismall.dao.IAccountDao">
    <!--查询所有-->
    <select id="findAll" resultType="account">
        select * from account;
    </select>
</mapper>

4.1.7编写测试类(UserTest和AccountTest)

  • AccountTest
public class AccountTest {
    private InputStream in;
    private SqlSession sqlSession;
    private IAccountDao accountDao;

    @Before//用于在测试方法执行之前执行
    public void init()throws Exception{
        //1.读取配置文件,生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.获取SqlSession对象
        sqlSession = factory.openSession(true);
        //4.获取dao的代理对象
        accountDao = sqlSession.getMapper(IAccountDao.class);
    }

    @After//用于在测试方法执行之后执行
    public void destroy()throws Exception{
        //提交事务
        // sqlSession.commit();
        //6.释放资源
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFindAll(){
        List<Account> accounts=accountDao.findAll();
        for(Account account:accounts){
            System.out.println(account);
        }
    }
}
  • UserTest
public class UserTest {

    private InputStream in;
    private SqlSession sqlSession;
    private IUserDao userDao;

    @Before//用于在测试方法执行之前执行
    public void init()throws Exception{
        //1.读取配置文件,生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.获取SqlSession对象
        sqlSession = factory.openSession(true);
        //4.获取dao的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After//用于在测试方法执行之后执行
    public void destroy()throws Exception{
        //提交事务
       // sqlSession.commit();
        //6.释放资源
        sqlSession.close();
        in.close();
    }

    /**
     * 测试查询所有
     */
    @Test
    public void testFindAll(){
        //5.执行查询所有方法
        List<User> users = userDao.findAll();
        for(User user : users){
            System.out.println(user);
        }

    }
}

4.2 如何进行多对一的查询?

  • 查询条件为,查询所有账户,同时还要获取当前账户所对应的用户信息(用户的名称和地址)

4.2.1使用Account子类的方式(不常用)

  • 1,在domain包中添加(AccountUser.java),此类继承Account类,目的是为了将获取的信息一部分封装到父类中(id,uid,money),一部分封装到本类中(属性:username,address)真棒!!!!!
package com.aismall.domain;

public class AccountUser extends Account{
    private String username;
    private String address;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return super.toString()+"      AccountUser{" +
                "username='" + username + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

  • 2,在IAccountDao接口中添加方法
 /*查询所有账户,同时还要获取当前账户所对应的用户信息(用户的名称和地址)*/
    List<AccountUser> findByAccount();
  • 3,在映射配置文件中添加配置(IAccountDao.xml)
  <!--查询所有账户,同时还要获取当前账户所对应的用户信息(用户的名称和地址)-->
    <select id="findByAccount" resultType="accountUser">
        select a.*,u.username,u.address from account a,user u where u.id=a.id;
    </select>
  • 4,在测试类中添加测试方法(AccountTest)
/*查询所有账户,同时还要获取当前账户所对应的用户信息(用户的名称和地址)*/
    @Test
    public void testFindByAccount(){
        List<AccountUser> accountUsers=accountDao.findByAccount();
        for (AccountUser accountUser:accountUsers){
            System.out.println(accountUser);
        }

    }

4.2.2从表实体(Account)包含主表实体(User)的引用方式(常用)

  • 1,在Account.java中添加属性,并提供该属性的get和set方法
private User user;
  • 2,在IAccountDao接口中添加方法
 /*查询用户*/
    List<Account> findAccountUserAll();
  • 3,在映射配置文件中添加配置(IAccountDao.xml)
<!--定义封装account和user的resultMap-->
    <resultMap id="accountUserMap" type="account">
        <!--account字段的封装-->
        <!--将account的id重名名为aid-->
        <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">
            <!--user表中的id还是id-->
            <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>
        </association>
    </resultMap>
    <!--查询所有-->
    <select id="findAccountUserAll" resultMap="accountUserMap">
        select a.*,u.username,u.address from account a,user u where u.id=a.id;
    </select>
  • 4,在测试类中使用添加测试方法(AccountTest.java)
 Test
public void testFindAccountUserAll(){
    List<Account> accounts=accountDao.findAccountUserAll();
    for(Account account:accounts){
        System.out.println("-----------------");
        System.out.println(account);
        System.out.println(account.getUser());
    }
}

4.3如何进行一对多的查询?

  • 需求:查询所有用户信息及用户关联的账户信息
  • 分析:
    用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,我们想到了左外连接查询比较合适。

4.3.1主表实体(User)包含从表实体(Account)的集合引用

  • 1,在User.java中添加属性并生成相应的set和get方法
/*一对多关系映射,主表实体应该包含从表实体的集合引用*/
private List<Account> accounts;
  • 2.在IUserDao接口中添加方法
/*一对多查询所有用户*/
    List<User> findUserAccountAll();
  • 3,在映射配置文件中添加配置(IUserDao.xml)
 <!--定义user的resultMap-->
    <resultMap id="UserAccountMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <result property="address" column="address"></result>
        <!--配置user对象中的account集合映射-->
        <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 user u left outer join account a on u.id=a.uid;
    </select>
  • 4,在测试类中使用添加测试方法(UserTest.java)
  /**一对多查询所有用户*/
    @Test
    public void testUserAccountAll(){
        List<User> users=userDao.findUserAccountAll();
        for(User u:users){
            System.out.println("----------------");
            System.out.println(u);
            System.out.println(u.getAccounts());
        }
    }

05mybatis多表查询之多对多

  • 多对多可以拆分为两个一对多

演示示例:用户和角色

  • 一个用户可以有多个角色
  • 一个角色可以赋予多个用户
    步骤:
  • 1、建立两张表:用户表,角色表,让用户表和角色表具有多对多的关系。需要使用中间表,中间表中包含各自的主键,在中间表中是外键。
  • 2、建立两个实体类:用户实体类和角色实体类,让用户和角色的实体类能体现出来多对多的关系,各自包含对方一个集合引用
  • 3、建立两个配置文件
    用户的配置文件
    角色的配置文件
  • 4、实现配置:
    当我们查询用户时,可以同时得到用户所包含的角色信息
    当我们查询角色时,可以同时得到角色的所赋予的用户信息

5.1实现从Role到User的多对多

  • 我们继续在上面那个工程的代码中进行改进

5.1.1 在数据库(myMybatis)中添加表

  • 多表查询会涉及到中间表,用户表之前已经创建了,我们只需要在之前的数据库中在加入两张表(角色表和角色用户中间表即可)
1.创建角色表(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,'校长','管理整个学校');

2.创建角色和用户的中间表
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);

5.1.2在包(domain)中创建实体类(Role.java)

  • 1,让此类实现Serializable 接口
  • 2,在类中添加以下属性,并生成get和set方法
  • 3,从Role到User的查询,Role实体类中要包含User属性
    因为是多对多的关系,所以是一个集合。
  • 注意:在生成toString方法时,不要包含users属性
 private  Integer roleId;
 private  String roleName;
 private  String roleDesc;
 //多对多的关系映射:一个角色可以赋予多个用户
 private List<User> users;

5.1.3在包(dao)中添加一个接口(IRoleDao)

  • 在接口中添加一个查询方法
public interface IRoleDao {
    /*查询所有方法*/
    List<Role> findAll();
}

5.1.4在包中添加映射配置文件(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.aismall.dao.IRoleDao">
    <!--定义 role 表的 ResultMap-->
    <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 column="id" property="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>
        </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>

5.1.5编写测试类(RoleTest.java)

public class RoleTest {
        private InputStream in;
        private SqlSession sqlSession;
        private IRoleDao roleDao;
        @Before//用于在测试方法执行之前执行
        public void init()throws Exception{
            //1.读取配置文件,生成字节输入流
            in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.获取 SqlSessionFactory
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            //3.获取 SqlSession 对象
            sqlSession = factory.openSession(true);
            //4.获取 dao 的代理对象
            roleDao = sqlSession.getMapper(IRoleDao.class);
        }
        @After//用于在测试方法执行之后执行
        public void destroy()throws Exception{
            //提交事务
            // sqlSession.commit();
            //6.释放资源
            sqlSession.close();
            in.close();
        }
        /**
         * 测试查询所有
         */
        @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());
            }
        }
    }

5.2实现从User到Role的多对多

  • 从 User 出发我们也可以发现一个用户可以具有多个角色,这样用户到角色的关系也还是一对多关系。
  • 这样我们就可以认为 User 与 Role 的多对多关系,可以被拆解成两个一对多关系来实现

5.2.1在主表(User)中添加从表(Role)属性

  • 添加属性并生成set和get方法
private List<Role> roles;

5.2.2在IUserDao接口中添加方法

/*查询所有*/
     List<User> findUserRoleAll();

5.2.3添加映射配置(IUserDao.xml)

 <!--定义user的resultMap-->
    <resultMap id="UserRoleMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <result property="address" column="address"></result>
        <!--配置user对象中的role集合映射-->
        <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="findUserRoleAll" resultMap="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.uid
        left outer join role r on r.id = ur.rid
    </select>

5.2.4在测试类(UserTest)中添加测试方法

@Test
    public void testFindUserRoleAll(){
        List<User> users=userDao.findUserRoleAll();
        for(User u:users){
            System.out.println("----------------");
            System.out.println(u);
            System.out.println(u.getRoles());
        }
    }

猜你喜欢

转载自blog.csdn.net/weixin_45583303/article/details/106408028