javaee之MyBatis框架3

news2025/2/3 8:50:46

mybatis中编写dao实现类的使用方式

简单说一下实现原理:

 下面来说一下UserDaoImpl的实现原理

mybatis主配置文件中properties标签的使用

第一种:

<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis1"></property>
<property name="username" value="root"></property>
<property name="password" value="5201314"></property>

 

第二种:resource属性引用外部配置文件

注意这个配置文件要放到类路径下,也就是src下面 

里面存放的就是相应的数据库连接信息

 

第三种:url属性统一资源定位符,也就是绝对路径

先来看一下这个文件位置 

file:///就是一种协议,window系统默认使用这种协议,打开资源都是基于file协议。端口也默认 

 mybatis主配置文件中typeAlias标签的使用

先来说第一种方式:

 

然后去修改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">

<!--配置每一个相应dao层的方法,写上相应dao的全类名-->
<mapper namespace="com.pxx.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>
    <!--Id代表方法名称 resultType:代表值类型
    这里写上它的全类名 -->
    <!--<select id="findAll" resultType="com.pxx.domain.User">-->
    <select id="findAll" resultMap="userMap">
       select * from user
       <!-- select id as userId,username as userName,address as userAddress,
       sex as userSex,birthday as userBirthday from user-->
    </select>

    <!--保存用户信息就不是select语句了
    而是insert语句-->
    <insert id="saveUser" parameterType="user">
        <!--配置插入操作之后,获取插入数据的id-->
        <selectKey keyProperty="userId" keyColumn="id" resultType="int" order="AFTER">
            select last_insert_id();
        </selectKey>
        insert into user(username,address,sex,birthday)values(#{userName},#{userAddress},#{userSex},#{userBirthday});
    </insert>

    <!--这里更新操作就是update-->
    <update id="updateUser" parameterType="user">
        update user set username=#{userName},address=#{userAddress},sex=#{userSex},birthday=#{userBirthday} where id=#{userId}
    </update>

    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id=#{id}
    </delete>

    <!--根据id查询用户的方法,又是查询操作-->
    <select id="findById" parameterType="java.lang.Integer" resultMap="userMap">
        select * from user where id=#{id}
    </select>

    <!--根据名称进行模糊查询操作
    但是模糊查询中like %名字% 中的这个百分号在在这里面体现不出来
    -->
    <select id="findByName" parameterType="string" resultMap="userMap">
        select * from user where username like #{username}
    </select>

    <!--查询用户总记录数-->
    <select id="findTotal" resultType="int" >
        select count(id) from user
    </select>

    <!--根据queryVo的条件查询用户-->
    <select id="findUserByVo" parameterType="com.pxx.domain.QueryVo" resultMap="userMap">
        select * from user where username like #{user.userName}
    </select>
</mapper>

下面说第二种方式:package

再来多说一点,package这个 标签还可以配置

 mybatis数据库连接池

数据库连接池原理

之前再说jdbc连接数据库的时候,我们用到了一个连接池技术Druid数据库连接池技术

DruidDataSourceFactory.createDataSource->这种方法会给我们产生一个数据库连接池

那么具体具体原理就是

用上面这张图来说明一下,首先连接池每一个连接,不可能同时被两个线程所拿到。然后假如线程1拿到2号连接,线程2拿到1号连接。那么连接池里面的连接就会重新编号,3号连接就变成1,然后依次往下面推,线程1,线程2进来的时候就变成7号,8号,也就是队列的对尾

Mybatis中的连接池

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


        type属性的取值:
              POOLED     采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
            UNPOOLED 采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池的思想。
            JNDI     采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样。
                 注意:如果不是web或者maven的war工程,是不能使用的。
                 我们课程中使用的是tomcat服务器,采用连接池就是dbcp连接池。
下面来对比一下POOLED连接池与UNPOOLED连接池的实现原理

先配置POOLED连接池来运行一下程序

在配置成UNPOOLED连接池来运行一下程序

 

然后我们去追一下UNPOOLD连接池源码

先来看看这两个配置分别对应的类

 

 我们ctrl+n来查找一个UnpooledDataSource这个类

 很明显,他就是一个实现了DataSource的类,这个DataSouce里面就有一个getConnection()方法去获得连接

 

下面我们说一下Pooled原理 

        

 同样实现了连接池接口

 

mybatis中的事务原理和自动提交设置

把下面配置成POOLED

 mybatis就是通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚

找到它的实现类

 

 然后点击去,会进入这个类 

当找不到一个类,就这样做,直接展示类图 

 

 

进入BaseExecutor找commit方法

 

进入到这个类 

 

 右键选择它的实现类

 

 

 

那能不能每次设置一个自动提交呢

 那我们这个时候去看一个类

现在去测试一下

 

 运行ok

动态sql

前面我们的SQL都是比较简单的,但是有时候业务逻辑复杂,我们的SQL是进行动态变化的。

拿到mybatis1进行改造,只保留查询方法与查询配置,那就先新建一个项目mybatis3,在这个里面保留查询方法与查询配置,或者不动也行

一、来说一下if标签的作用

首先在dao层增加一个这样的接口

然后在IUserDao.xml进行一个配置

在test里面测试一个方法

 它的封装原理就是

 运行结果:

如果我们又多加了一个参数sex="女"

 

 二、where标签的使用

上面就是我们去简化where 后面1 == 1的这个作用

下面贴上where之后,就会给我们自动and,然后匹配条件 

 查询一样没问题

三、foreach和sql标签的使用

先来贴一下foreach的用法

 

在dao层添加一个方法

 

 我们想要做的就是:

先去修改一下QueryVo对象里面的属性与方法

 

现在去配置一下IUserDao.xml里面的信息

 下面去Test类里面写这个方法

运行结果:

 三、下面说一下sql标签的使用

这个方法的作用是抽取sql片段

 

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">

<!--配置每一个相应dao层的方法,写上相应dao的全类名-->
<mapper namespace="com.pxx.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>

    <!--
    抽取重复的sql语句
    -->
    <sql id="defaultUser">
        select * from user
    </sql>
    <!--Id代表方法名称 resultType:代表值类型
    这里写上它的全类名 -->
    <!--<select id="findAll" resultType="com.pxx.domain.User">-->
    <select id="findAll" resultMap="userMap">
       <!--select * from user
       这里用sql标签代码替换掉
       -->
       <include refid="defaultUser"></include>
       <!-- select id as userId,username as userName,address as userAddress,
       sex as userSex,birthday as userBirthday from user-->
    </select>

    <!--保存用户信息就不是select语句了
    而是insert语句-->
    <insert id="saveUser" parameterType="user">
        <!--配置插入操作之后,获取插入数据的id-->
        <selectKey keyProperty="userId" keyColumn="id" resultType="int" order="AFTER">
            select last_insert_id();
        </selectKey>
        insert into user(username,address,sex,birthday)values(#{userName},#{userAddress},#{userSex},#{userBirthday});
    </insert>

    <!--这里更新操作就是update-->
    <update id="updateUser" parameterType="user">
        update user set username=#{userName},address=#{userAddress},sex=#{userSex},birthday=#{userBirthday} where id=#{userId}
    </update>

    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id=#{id}
    </delete>

    <!--根据id查询用户的方法,又是查询操作-->
    <select id="findById" parameterType="java.lang.Integer" resultMap="userMap">
        select * from user where id=#{id}
    </select>

    <!--根据名称进行模糊查询操作
    但是模糊查询中like %名字% 中的这个百分号在在这里面体现不出来
    -->
    <select id="findByName" parameterType="string" resultMap="userMap">
        select * from user where username like #{username}
    </select>

    <!--查询用户总记录数-->
    <select id="findTotal" resultType="int" >
        select count(id) from user
    </select>

    <!--根据queryVo的条件查询用户-->
    <select id="findUserByVo" parameterType="com.pxx.domain.QueryVo" resultMap="userMap">
        select * from user where username like #{user.userName}
    </select>

    <!--
    根据条件来得到一个查询结果
    -->
    <!--<select id="findUserByCondition" resultMap="userMap" parameterType="user">
        select * from user where 1=1
        <if test="userName != null">
            and username = #{userName}
        </if>
        <if test="userSex != null">
            and sex = #{userSex}
        </if>
    </select>-->

    <select id="findUserByCondition" resultMap="userMap" parameterType="user">
        <!--select * from user-->
        <include refid="defaultUser"></include>
        <where>
        <if test="userName != null">
            and username = #{userName}
        </if>
        <if test="userSex != null">
            and sex = #{userSex}
        </if>
        </where>
    </select>

    <select id="findUserInIds" resultMap="userMap" parameterType="queryvo">
        <!--select * from user-->
        <include refid="defaultUser"></include>
        <where>
            <if test="ids != null and ids.size()>0">
                <!--
                利用foreach来进行一个参数的封装
                -->
                <foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
                    #{uid}
                </foreach>
            </if>
        </where>
    </select>



</mapper>

 运行结果:

 Mybatis的多表操作

一、下面来分析一下表与表之间的关系

 看一下数据库中account表与user表之间的关系

 看一下上面两张表的构建

account表

user表

 

 上面的account中的uid与user表中的id,前者是从表,后者是主表,前者被后者约束

 uid可以有多个相同的id值匹配

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

一个user匹配了多个账户,换句话说,一个账户拿出来只会匹配一个用户

也就是多对一的一个查询,变成一对一的查询

1.先来定义一个账户信息的实体类

 

Account.java

package com.pxx.domain;

public class Account {

    private Integer id;
    private Integer uid;//这格个是从键,user表的id是主键
    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 +
                '}';
    }
}

 2.编写sql语句

实现查询账户信息时,也要查询账户所对应的用户信息。这里是查询所有信息。

现在定义一个封装类AccountUser来封装上面的数据 

 

AccountUser.java

package com.pxx.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 "AccountUser{" +
                "username='" + username + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

上面就会给我们返回一个查询结果

我们需要把这个查询结果封装到一个对象里面去

 3.定义账户的持久层Dao接口

因为每一个dao基本上就是操作一张表,这里是account表,就是IAccountDao接口

 

 

4.现在来定义AccountDao.xml的配置信息

 为什么说现在不需要去配置SqlMapConifg.xml中mappers中映射一个每一个dao文件的xml配置文件

因为这里已经用package进行了配置

 好了,不说那么多,继续配置AccountDao.xml文件

 

 下面开始去写测试类

 

运行结果

上面这种采用一个AccountUser去继承Account显示是不行的,也违背了软件的合成复用原则,它的意思就是尽量使用对象组合、聚合,而不是使用继承关系达到代码的复用的目的

下面我们采用另外一种方式来做

首先在account里面聚合一个user类,因为要查询出user类的username和address信息

换句话说,就是从表的实体应该包含一个主表实体的对象引用

在Account对象里面

 现在我们去修改IAccountDao.xml文件,在修改这个.xml文件之前,我们要先去修改User.java文件

User.java

package com.pxx.domain;

import java.util.Date;

public class User {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    public Integer getId() {
        return id;
    }

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

    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;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}

现在再去配置一下IAccountDao.xml中的数据封装信息

现在去dao层是写一个操作接口

IAccountDao.java

现在去相应的.xml配置文件中去配置一下

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.pxx.dao.IAccountDao">
    <!--定义封装的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的内容
        以为account里面聚合的一个user类,所以
        不能直接向上面一样result
        -->
        <association property="user" column="uid" 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>


    <!--配置查询所有操作
    查询一个账户同时包含了用户名和地址信息
    -->
    <select id="findAllAccount" resultType="accountuser">
        select a.*,u.username,u.address from account a,user u where u.id = a.uid
    </select>
</mapper>

解释一下两个地方

 

下面去看一下AccountTest.java里面代码

package com.pxx.test;

import com.pxx.dao.IAccountDao;
import com.pxx.dao.IUserDao;
import com.pxx.domain.Account;
import com.pxx.domain.AccountUser;
import com.pxx.domain.QueryVo;
import com.pxx.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class AccounTest {
    //把这些变量先全部做成全局变量
    private InputStream in;
    private SqlSession sqlSession;
    private IAccountDao accountDao;

    @Before
    public void init() throws IOException {
        //1.读取配置文件
        //这个Resources是Mytais给我们提供的一个类,直接加载资源文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.使用工厂生产SqlSession对象,这个可以理解为dao的实现类
        sqlSession = factory.openSession(true);
        //4.使用SqlSession创建Dao接口的代理对象
        accountDao = sqlSession.getMapper(IAccountDao.class);
    }

    @After
    public void destroy() throws IOException {
        // sqlSession.commit();
        sqlSession.close();
        in.close();
    }

    /**
     * 测试查询所有信息
     */
    @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());
        }
    }


    /**
     * 测试查询所有账户,同时包含用户名称和地址
     */
    @Test
    public void testFindAllAccount() {
        List<AccountUser> aus = accountDao.findAllAccount();
        for(AccountUser au : aus) {
            System.out.println(au);
        }
    }


}

 运行结果:

总结关于:多对一的一个查询

 三、关于一对多的查询操作

1.还是拿到这个来分析一把

2.编写sql语句

这里采用左外连接的用法

 

拿到左边去整体匹配一遍右表的数据,如果有匹配则显示出来,不要这条记录也显示,但是只是游右表上面相应字段的数据显示为null

 

 

3.做一对多,在主表User类中聚合一个从表Accoount类

User.java

package com.pxx.domain;

import java.util.Date;
import java.util.List;

public class User {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    private List<Account> accounts;

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

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

    public Integer getId() {
        return id;
    }

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

    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;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                ", accounts=" + accounts +
                '}';
    }
}

 4.在IUserDao接口中加入方法

5.下面去相关的dao文件中进行配置IUserDao.xml

这里说一下在多对一的时候,我们在.xml文件中做了如下的相关映射

 那么一对多也要做一个相关类型的配置,才能映射相关字段信息

 下面重新写一个测试方法UserTest

package com.pxx.test;

import com.pxx.dao.IAccountDao;
import com.pxx.dao.IUserDao;
import com.pxx.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class UserTest {
    //把这些变量先全部做成全局变量
    private InputStream in;
    private SqlSession sqlSession;
    private IUserDao userDao;

    @Before
    public void init() throws IOException {
        //1.读取配置文件
        //这个Resources是Mytais给我们提供的一个类,直接加载资源文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.使用工厂生产SqlSession对象,这个可以理解为dao的实现类
        sqlSession = factory.openSession(true);
        //4.使用SqlSession创建Dao接口的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After
    public void destroy() throws IOException {
        // sqlSession.commit();
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFindAllUser() {
        List<User> users = userDao.findAllUser();
        for(User user : users) {
            System.out.println("-------每个用户的内容---------");
            System.out.println(user);
            System.out.println(user.getAccounts());
        }
    }
}

 运行结果:

Mybatis多表查询之多对多

一、role与user表的多对多

有时候也是看成一对多或者多对一来考虑的

比如说角色和用户,那么一个角色,就有多个用户,1个用户也有多个角色

1.先来分析一下表与表之间的关系,多对多,这里会涉及到第三张表

 2.编写具体的SQL语句

 3.编写角色实体类

我们需要是是不是下面两张表的信息

 所以这里其实是一个1对多的查询,这个时候类的设计我们就把从表放到主表里面

也就是把user聚合到role里面

Role.java

package com.pxx.domain;

import java.util.List;

public class Role {
    private int roleId;
    private String roleName;
    private String roleDesc;

    //在主表引入一个从表
    private List<User> users;

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

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

    public int getRoleId() {
        return roleId;
    }

    public void setRoleId(int roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleDesc() {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }

    @Override
    public String toString() {
        return "Role{" +
                "roleId=" + roleId +
                ", roleName='" + roleName + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                ", users=" + users +
                '}';
    }
}

 4.编写Role表的dao层接口

 5、现在要给这张表的dao层准备一个.xml的配置文件

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.pxx.dao.IRoleDao">
    <!--
        这个里面还是涉及到了一个对象包含了另外一个对象
        这里就要做一个属性字段映射
        当多对一的时候,采用<association>
        这里一对多,就采用<collection>配置对象里面的对象
    -->
    <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>
        <!--开始配置user类
        property:映射到role类上面的饿某个属性上面
        ofType:把结果集封装到哪一个类上面
        -->
        <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 r.*,u.id uid,u.username username,u.birthday birthday,u.sex sex,u.address address 
        FROM role r 
        INNER JOIN user_role ur ON (r.id = ur.rid)
        INNER JOIN USER u ON (ur.uid = u.id)
    </select>
</mapper>

6、现在开始编写测试类

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/103801.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件加密保护:Mirage License Protector v5 注册版

Mirage License Protector v5.1.0 适用于许多编译器 License Protector 是一个 DLL / COM exe&#xff0c;几乎可以与 所有编译器一起使用&#xff1a;C、C#、.NET、Delphi、VB6 和 VB.NET&#xff0c; 32 位和 64 位的 Java 哪种产品适合我&#xff1f; 选择许可证保护器 应…

智能无障碍轮椅——PID算法控制

文章目录一、PID算法二、控制方法对比棒棒控制&#xff08;启停控制器&#xff09;比例控制PI控制PID控制三、PID的手动整定四、PID衰减曲线整定五、PID调节器各校正环节的作用是&#xff1a;六、PID算法的一般表达式是&#xff1a;七、计算注意事项一、PID算法 PID控制是最早…

图像语义分割网络FCN(32s、16s、8s)原理及MindSpore实现

一、FCN网络结构 全卷积网络(Fully Convolutional Networks)&#xff0c;是较早用于图像语义分割的神经网络。根据名称可知&#xff0c;FCN主要网络结构全部由卷积层组成&#xff0c;在图像领域&#xff0c;卷积是一种非常好的特征提取方式。本质上&#xff0c;图像分割是一个分…

DBCO-PEG-NH2/Amine二苯基环辛炔-聚乙二醇-氨基 简介。用于以高特异性和反应性标记叠氮化物修饰的生物分子。

中文名称&#xff1a; 二苯基环辛炔-聚乙二醇-氨基 氨基-聚乙二醇-二苯基环辛炔 英文简称&#xff1a; DBCO-PEG-NH2/Amine Amine/H2N-PEG-DBCO 外观&#xff1a; 灰白色固体或半固体&#xff0c;取决于PEG的分子量 溶剂&#xff1a; 部分常规有机溶剂 存储…

day20-django

文件上传 批量上传数据 案例&#xff1a;混合数据(Form) 提交页面&#xff1a;用户输入数据文件&#xff08;输入不能为空&#xff0c;报错&#xff09; django开发过程中两个特殊的文件夹 static&#xff1a;存放静态文件的路径&#xff0c;包括css、js、项目图片 media&…

【问题记录】Git问题记录

文章目录问题1Failed to connect to github.com port 443 after 21085 ms: Timed outOpenSSL SSL_read: Connection问题2unable to access https://github.com//: OpenSSL SSL_read: Connection was reset, errno 10054网速慢问题clone太慢&#xff0c;pull太慢怎么办问题1 Fa…

记录C,C++关键字的位置,直接跳过注释和字符串文本。

依据第二版本&#xff0c;可以写一个跳过注释的查找函数 C_IndexOfWord Java_IndexOfWord CSharp_IndexOfWord 还有一种方法&#xff0c;可以先把所有注释用空格代替&#xff0c;查出的字符位置也不变。 以前版本&#xff1a; DList<TextColor> Syntax::GetTextColor…

m基于matlab的连续相位调制(CPM)解调系统仿真,包括解调,同步等模块

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 随着数字通信技术的飞速发展&#xff0c;数字通信的调制技术也得到了快速发展&#xff0c;其中连续相位调制(Continuous Phase Modulation&#xff0c;CPM)作为一种全新的通信调制方法得到了越来…

GNN 极简入门

文章目录图基本知识GNN简介GCNPYG极简入门Data Handling of GraphsCommon Benchmark DatasetsMini-batchesData TransformsLearning Methods on Graphs图基本知识 &#x1f638;图是由节点的有穷非空集合和节点之间边的集合组成&#xff0c;通常表示为 G(V,E)G(V, E)G(V,E)&am…

涨知识系列:爆款短视频拍摄技巧之一,构图

拍摄技巧主要分为两个部分&#xff0c;一个是构图&#xff0c;一个是拍摄手法。这部分内容其实也是比较简单的&#xff0c;因为短视频毕竟不需要做到像拍电影那么专业。所以在短视频当中我们只要学会一些基本的技巧就完全够用了。更重要的是我们需要对构图和拍摄手法有一个概念…

基于KT6368A的双模蓝牙模块打印机的方案

目录 一、打印机蓝牙模块简介 目前主流的打印机&#xff0c;很多都还是不带蓝牙。大部分的受限于成本等等原因&#xff0c;都还是通过USB的方式和电脑进行通讯&#xff0c;从而完成打印的数据交互 因为早期蓝牙技术发展的比较缓慢&#xff0c;而打印机类型的产品&#xff0c;…

_12LeetCode代码随想录算法训练营第十二天-C++二叉树

_12LeetCode代码随想录算法训练营第十二天-C二叉树 二叉树基础知识 二叉树的种类 满二叉树 满二叉树&#xff1a;如果一棵二叉树只有度为0的结点和度为2的结点&#xff0c;并且度为0的结点在同一层上&#xff0c;则这棵二叉树为满二叉树。 完全二叉树 完全二叉树的定义如下…

线段树能解决多少问题?

背景 给一个两个数组&#xff0c;其中一个数组是 A [1,2,3,4]&#xff0c;另外一个数组是 B [5,6,7,8]。让你求两个数组合并后的大数组的&#xff1a; 最大值最小值总和 这题是不是很简单&#xff1f;我们直接可以很轻松地在 O(mn) 的时间解决&#xff0c;其中 m 和 n 分别为…

maven的java工程获取mysql数据库数据【问题及解决过程记录】

创建数据库maven&#xff0c;指定字符集和排序规则 UTF8MB4常用的排序规则&#xff1a;utf8mb4_unicode_ci、utf8mb4_general_ci、utf8mb4_bin&#xff0c;选用哪种方式呢&#xff1f;先来分析一下&#xff1a; 1、准确性&#xff1a; &#xff08;1&#xff09;utf8mb4_unico…

ffmpeg-时间基tbn、tbc、tbr

时间基的作用 源码来自ffmpeg5.1。 时间基在ffmpeg中是通过数据结构有理数AVRational描述的。时间基为时间戳的单位&#xff0c;比如时间基tbn(AVStream.time_base)0.001秒&#xff0c;AVPacket的pts40&#xff0c;则表明该AVPacket要在tbn*pts0.04秒开始显示。 /** 代码路径…

JavaScript 网页特效

一、Offset 1.1 概述 offset > 偏移量 &#xff0c;可以动态的获取的元素的位置、大小等属性。 获得元素距离带有定位父元素的位置获得元素自身的大小(宽度高度) 返回的数值都不带单位 offset常用属性&#xff1a; 属性作用element.offsetParent返回作为该元素带有定位…

全球汽车后行业发展现状:欧洲市场保持稳健 中国产业规模增速较快

根据观研报告网发布的《2022年中国汽车后市场分析报告-市场发展格局与投资潜力研究》显示&#xff0c;汽车后市场&#xff08;AM市场&#xff09;是指汽车在销售之后维修和保养服务及其所包含的汽车零部件、汽车用品和材料的交易市场&#xff0c;它涵盖了消费者买车后所需要的一…

LeetCode 321 周赛

2485. 找出中枢整数 给你一个正整数 n &#xff0c;找出满足下述条件的 中枢整数 x &#xff1a; 1 和 x 之间的所有元素之和等于 x 和 n 之间所有元素之和。 返回中枢整数 x 。如果不存在中枢整数&#xff0c;则返回 -1 。题目保证对于给定的输入&#xff0c;至多存在一个中…

STM32单片机直流电机PID速度控制正反转控制(霍尔磁铁测速)LCD1602

实践制作DIY- GC0116-直流电机PID速度控制 一、功能说明&#xff1a; 基于STM32单片机设计-直流电机PID速度控制 功能介绍&#xff1a; STM32F103C系列最小系统LCD1602直流电机磁铁霍尔传感器MX15系列驱动模块4个按键&#xff08;速度减、速度加、开/关、正转/反转&#xff0…

【复习笔记】【嵌入式】嵌入式系统及其原理复习重点——篇二

嵌入式系统及其原理复习重点笔记 2 ARM处理器和指令集 ARM处理器简介 ARM架构与ARM处理器对应关系 V1版架构 该版架构只在原型机ARM1出现过,处理能力有限&#xff0c;其基本性能&#xff1a; 寻址空间&#xff1a;64M字节(26位)基本的数据处理指令(无乘法)字节、半字和字的…