MyBatis(02)

MyBatis复杂映射

上次课程中实现的Sql语句都是比较简单的

简单sql语句可以直接使用注解编写在接口中的方法上

但是如果sql语句比较复杂,注解的方式功能就比较有限了

需要使用Xml文件来支持功能更强大的查询

在mapper.xml文件中 “<=” 与 “<” 与 “>=” 与 “>” 的书写方式

Mybatis中的sql语句中的“<”和“>”号要用转义字符“<”和”>“,不能直接写"<“或”>",否则会报错!

示例:
小于➡"<“➡”<"
大于➡">“➡”>"
小于等于➡"<=“➡”<="
大于等于➡">=“➡”>="

使用Xml文件来映射sql语句

步骤1:

实现Xml映射的sql语句也是在接口中编写一个方法

但是这个方法不需要加注解

public interface UserMapper {

    //xml映射sql语句的方法
    //不需要在方法上写注解
    String getUsernameById();

	///......
}

步骤2:

在resources文件夹下新建一个mappers的文件夹

其中新建一个UserMapper.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">
<!-- namespace 属性执行映射到 UserMapper接口 -->
<mapper namespace="cn.tedu.mapper.UserMapper">

    <!-- 需要将返回值类型声明给SqlSessionFactory -->
    <!-- 使用resultType指定类型,类型需要使用全类名表示  -->
    <select id="getUsernameById" resultType="java.lang.String">
        select username from t_user where id=1
    </select>
</mapper>

步骤3:

将我们配置xml文件的路径记录在jdbc.properties文件中,以便配置类使用

db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/tedu_ums?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
db.username=root
db.password=root
db.maxActive=10
db.initialSize=2
#新增的配置!!!!!!!!
mybatis.mapper.location=classpath:mappers/*.xml

步骤4:

在MybatisConfig类中修改对SqlSessionFactory类的注入

 @Bean
    public SqlSessionFactory sqlSessionFactory(
            DataSource dataSource,
            //使用@Value指定xml文件的位置,并赋值给Resource数组
            @Value("${mybatis.mapper.location}")
            Resource[] mapperLocations
            ) throws Exception {
        SqlSessionFactoryBean bean=
                new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(mapperLocations);
        return bean.getObject();
    }

步骤5:

测试

 @Test
    public void testHelloXml(){
        UserMapper mapper=ctx.getBean(
                "userMapper",UserMapper.class);
        String username=mapper.getUsernameById();
        System.out.println(username);
    }

方法的可变参数

可变参数方法的定义

 //可变参数方法的定义
    //在参数列表中参数类型后加...表示这个参数是可变参数
    //1.一个方法只能有一个可变参数,而且必须是这个方法的最后一个参数
    //2.在方法内部处理这个参数时,将这个参数视为一个数组
    public static void  sum(int... nums){
        //假设这个方法的业务是计算所有参数之和
        int sum=0;
        for(int i=0;i<nums.length;i++){
            sum+=nums[i];
        }
        System.out.println(sum);
    }

可变参数方法的调用

 public static void main(String[] args) {
        //可变参数方法调用事项
        //1.可变参数的数量可以是0个,1个或多个
        //2.可变参数的位置可以传入该类型的数组
        sum();
        sum(100);
        sum(8,10,20);
        int[] a={5,9,1,2,4,2,3,7};
        sum(a);
    }

使用Xml处理动态Sql语句

动态foreach

如果现在要一次性根据id删除多条记录

怎么操作?

如果使用按id删除一条记录的方法来遍历删除,由于连库次数多,删除效率一定低

我们可以使用动态sql语句的foreach来动态生成SQL语句删除记录

实现一条sql语句删除多行:

delete from t_user where id in (?,?,?)

直接使用可变参数指定要删除的id

UserMapper方法定义如下

//动态foreach删除
    Integer deleteById(Integer... ids);

xml文件中代码如下

<!-- 动态foreach删除  -->
    <delete id="deleteById">
        delete from t_user where id in (
            <!-- array就是数组类型参数,当参数中只有唯一数组类型时,自动赋值 -->
            <!-- item表示从数组\集合中获取出的元素 -->
            <!-- separator是多个元素之间的分隔符-->
            <foreach collection="array" item="id" separator=",">
                #{id}
            </foreach>
        )
    </delete>

<foreach>标签还有两个属性

分别是open和close

表示循环开始之前和结束之后加什么内容

显示运行\生成的sql语句

我们希望能够看到我们自己编写的sql运行的状态

所以我们可以利用log4j日志记录的功能将Mybatis运行的过程输出到控制台

步骤1:

首先保证有log4j的依赖

<dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.25</version>
</dependency>

步骤2:在resources文件夹在新增log4j.properties配置文件

log4j.rootLogger=ERROR, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

# MyBatis Mapper
log4j.logger.cn.tedu.mapper=TRACE

动态分支结构

在Mybatis的Xml文件中除了可以进行循环

还可以进行分支结构

即当满足指定条件时添加指定sql语句

实现方式有if标签和choose标签

<if test="表达式">
   sql语句片段
</if>

Mybaits中没有支持if标签的else标签只能手动编写条件表达式的反表达式

如果非要类似else标签的支持,可以选用choose标签

<choose>
	<when test="表达式">
		当条件满足时生成的sql语句片段
	</when>
	<!--  允许编写多个when标签  -->
	<otherwise>
		当所有when表达式都没有满足时生成的sql片段
	</otherwise>
</choose>

下面我们来实现几个具体的业务

回忆一下我们上次课讲的全行修改

UPDATE t_user 
SET username=#{username},  
    password=#{password}, 
    age=#{age}, 
    phone=#{phone}, 
    email=#{email} 
WHERE id=#{id}

全行修改这个业务实际开发中几乎不会用到

但是对其中某一个列修改的业务又非常常见

我们不希望为买个列的单独修改都编写一个方法,所以希望编写一个方法

根据参数的情况,来修改不同的列

在UserMapper接口中编写代码如下

 //动态 if 修改
    Integer updateUserInfo(User user);

在UserMapper.xml文件中编写

<update id="updateUserInfo" parameterType="cn.tedu.entity.User">
        UPDATE t_user
        <!-- set标签会生成set关键字,还会自动去掉动态if部分产生的最后一个, -->
        <set>
            <!-- 当某个属性不为空时,表示要修改这个属性的值 -->
            <if test="username != null">
                username=#{username},
            </if>
            <if test="password !=null">
                password=#{password},
            </if>
            <if test="age != null">
                age=#{age},
            </if>
            <if test="phone != null">
                phone=#{phone},
            </if>
            <if test="email != null">
                email=#{email}
            </if>
        </set>
        WHERE id=#{id}
    </update>

测试一下

@Test
    public void testUpdate(){
        UserMapper mapper=ctx.getBean(
                "userMapper",UserMapper.class);
        User user=new User();
        user.setId(10);
        //user.setEmail("frank02@qq.com");
        //user.setAge(28);
        user.setPassword("4321");
        user.setPhone("18055500555");
        Integer num=mapper.updateUserInfo(user);
        System.out.println(num);
    }

动态参数查询

我们能经常看到下面的查询界面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-izopQF9R-1607340542978)(pvboxAhOLtSVXjRk.png!thumbnail)]

如果三个条一个都不写直接点查询:

执行

select * from t_user

如果写其中的一个条件点查询:

可能

select * from t_user where username like ?
select * from t_user where age=?
select * from t_user where phone like ?

如果写了其中两个或更多条件则与运行

select * from t_user where username like ? and age=?
.....

显然这个又一种动态sql语句拼接的情况

首先编写接口方法

//动态id 查询
    List<User> findUsersByParam(
            @Param("username")String username,
            @Param("age")Integer age,
            @Param("phone") String phone
    );

UserMapper.xml文件中

<!-- 动态查询  -->
    <select id="findUsersByParam" resultType="cn.tedu.entity.User">
        select
            id,username,password,age,phone,email
        from
            t_user
        <where>
            <if test="username != null">
                username like #{username}
            </if>
            <if test="age != null">
                and age = #{age}
            </if>
            <if test="phone != null">
                and phone like #{phone}
            </if>
        </where>
    </select>

测试代码

@Test
    public void testSelect(){
        UserMapper mapper=ctx.getBean(
                "userMapper",UserMapper.class);
        List<User> list=mapper.findUsersByParam(
                "F%",null,"13%");
        for(User u: list){
            System.out.println(u);
        }

    }
<?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">
<!-- namespace 属性执行映射到 UserMapper接口 -->
<mapper namespace="cn.tedu.mapper.UserMapper">
    <!--需要将返回值类型声明给SqlSessionFactory-->
    <!--使用resultType来指定类型,类型需要使用全类名表示 -->
    <select id="getUsernameById" resultType="java.lang.String">
        select username from t_user where id=1
    </select>
    <delete id="deleteByIds">
        delete from t_user where id in (
            <!--array就是数组类型的参数,当参数只有唯一数组类型时,自动赋值-->
            <!--item表示从数组/集合中获取出的元素,名字自定义-->
            <!--separator是多个元素之间的分隔符-->
            <foreach collection="array" item="id" separator=",">
                #{id}
            </foreach>
        )
    </delete>

    <update id="updateUserInfo" parameterType="cn.tedu.entity.User">
        update t_user
        <!--set标签会生成set关键字,还会自动去掉动态if部分产生的最后一个逗号","-->
        <set>
            <!--当某个属性不为空时,表示要修改这个属性的值-->
            <if test="username !=null">
                username=#{username},
            </if>
            <if test="password !=null">
                password=#{password},
            </if>
            <if test="age !=null">
                age=#{age},
            </if>
            <if test="phone !=null">
                phone=#{phone},
            </if>
            <if test="email !=null">
                email=#{email}
            </if>
        </set>
        where
            id=#{id}
    </update>

    <!--   动态查询  -->
    <select id="findUserByParam" resultType="cn.tedu.entity.User">
        select
            id,username,password,age,phone,email
        from
            t_user
        <where>
            <if test="username !=null">
                username like #{username}
            </if>
            <if test="age !=null">
                and age like #{age}
            </if>
            <if test="phone !=null">
                and phone like #{phone}
            </if>
        </where>
    </select>

    <select id="findUserDepartment" resultType="cn.tedu.vo.UserVO">
        select
            u.id,
            username,
            d.name as departmentname
        from
            t_user u
        left join
            t_department d
        on
            u.department_id=d.id
    </select>


</mapper>

表关系基础

数据库中的多张表可能存在关联关系

比如

公司部门表(t_department)和员工表(t_user)

  1. 一个部门有多个员工,一个员工只能属于一个部门

    这样情况就叫一对多(多对一)

    上面的情况是数据库中最常见的表关系

  2. 一个人只有一张身份证和一份档案

    一张身份证只能对应一份档案

    一份档案中只保存一个身份信息

    这种情况就是一对一

    3.在中小学校中,老师和学生的关系

​ 一个老师教多个学生

​ 一个学生被多个老师教

​ 上面的情况就是多对多

Mybatis实现关联查询

关联查询准备工作

我们创建一个部门表,再利用现有的t_user表形成一个一对多的关系

-- 创建 t_department 表
CREATE TABLE t_department (
 	id INT(11) AUTO_INCREMENT COMMENT '部门ID',
 	name VARCHAR(20) NOT NULL COMMENT '部门名',
 	PRIMARY KEY (id)
)DEFAULT CHARSET=utf8;

为表赋值

-- 在t_department表中插入数据
INSERT INTO t_department (name) VALUES ('Java'), ('C++'), ('Linux');

为t_user表新加一个部门id列

alter table t_user add column department_id int

为新加的列赋值

UPDATE t_user SET department_id=1 WHERE id<=7;

UPDATE t_user SET department_id=2 WHERE id>7;

添加外键(可选)

ALTER TABLE t_user
ADD CONSTRAINT dept_user FOREIGN KEY (department_id)
REFERENCES t_department(id)

使用值对象(vo)处理关联查询

现在需求查询如下信息

查询上面呃信息需要如下sql语句

SELECT u.id,username,d.name
 FROM t_user u
 LEFT JOIN  t_department d
 ON u.department_id=d.id

这样的查询结果,我们没有合适的实体类能接收

简单来讲我们可以为了这次查询创建一个合适的实体类

这种实体类称之为Vo(值对象)

创建cn.tedu.vo包,包中新建UserVO类

public class UserVO implements Serializable {
    private Integer id;
    private String username;
    private String departmentName;
	// 省略get\set\toString    
    
}

UserMapper接口新加方法

//关联查询(值对象查询)
List<UserVO> findUserDepartment();

UserMapper.xml文件

<select id="findUserDepartment" resultType="cn.tedu.vo.UserVO">
        SELECT
            u.id,
            username,
            d.name departmentname
        FROM
            t_user u
        LEFT JOIN
            t_department d
        ON
            u.department_id=d.id
    </select>

测试类代码

 // 关联查询(值对象)
    @Test
    public  void  getUserVO(){
        UserMapper mapper=ctx.getBean(
                "userMapper",UserMapper.class);
        List<UserVO> list=mapper.findUserDepartment();
        for(UserVO vo :list){
            System.out.println(vo);
        }
    }

使用一的一方处理关联查询

下面我们如果想查询出如下结果
一张表的id和name,另一张表的对象的属性

我们先创建cn.tedu.entity.Department

public class Department implements Serializable {

    private Integer id;
    private String name;

    private List<User> users;
    //省略get\set\toString
   
}

我们要执行的sql语句如下

按部门id查询这个部门信息和这个部门的所有员工

SELECT *
FROM t_department d
LEFT JOIN t_user u
ON d.id=u.department_id
WHERE d.id=1

由于下面的操作针对的是Department类,所以应该创建Department对应的Mapper和xml

创建DepartmentMapper接口,编写代码如下

public interface DepartmentMapper {

    //按id查询部门以及部门员工的信息
    Department findDeptWithUserById(Integer id);
}

创建DepartmentMapper.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="cn.tedu.mapper.DepartmentMapper">

    <!-- 下面是一个resultMap的自定义映射 -->
    <!--可以编写一个\多个查询结果中数据查询的列名对应java的属性名的关系 -->
    <resultMap id="deptMap" type="cn.tedu.entity.Department">
        <!-- 每个result标签表示一个列和一个java属性的对应关系 -->
        <result column="id" property="id"/>
        <result column="name" property="name"/>
        <!-- 当实体类中有List或类似集合类型对象时 -->
        <!-- 就使用collection来映射 -->
        <!-- ofType表明集合的泛型类型,property就是属性名 -->
        <collection property="users" ofType="cn.tedu.entity.User">
            <!--下面的映射对应的是User类的属性-->
            <result column="userId" property="id" />
            <result column="username" property="username"/>
            <result column="password" property="password"/>
            <result column="age" property="age"/>
            <result column="phone" property="phone"/>
            <result column="email" property="email"/>
        </collection>
    </resultMap>

    <select id="findDeptWithUserById" resultMap="deptMap">
        SELECT
            d.id,
            name,
            u.id userId,
            username,
            password,
            age,
            phone,
            email
        FROM t_department d
        LEFT JOIN t_user u
        ON d.id=u.department_id
        WHERE d.id=#{id}
    </select>
</mapper>

测试

 //关联查询(映射集合)
    @Test
    public void selectList(){
        DepartmentMapper mapper=ctx.getBean(
                "departmentMapper",DepartmentMapper.class);
        Department d=mapper.findDeptWithUserById(1);
        System.out.println(d);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值