MyBatis的CRUD操作

实现对数据库的操作主要依靠一下几个步骤:

  1. 创建实体类(Entity)
  2. 创建Mapper XML,编写SQL语句
  3. 在配置文件中新增mapper配置
  4. 调用sqlSession方法执行SQL语句

先创建一个实体类

package com.mybatis.entity;

public class Good {
    private Integer goodsId;
    private String title;
    private String subTitle;
    private Float originalCost;
    private Float currentPrice;
    private Float discount;
    private Integer isFreeDelivery;
    private Integer categoryId;

    public Good() {
    }
	
	// 省略Get/Set方法和toString方法
	......
}

SELECT

结果集查询

  1. 在Resource资源文件夹中创建XML文件,编辑SQL脚本
<?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="good">
    <select id="selectGoods" resultType="com.mybatis.entity.Good">
        SELECT * FROM t_goods ORDER BY goods_id DESC LIMIT 10
    </select>

</mapper>

namespace指定该XML的空间命名用于标识该文件的唯一性,空间命名有两种写法,一种是短命名,例如selectAll,使用短命名时如果项目中有相同名称时,就无法使用短名称来调用该xml文件;另一种为全限定名,即com.mypackage.MyMapper.selectAllThings将被直接用于查找及使用。

select标签的id属性用于标识XML中的唯一语句,resultType即查询结果所对应的实体类

myBatis的便捷就在于将代码与SQL语句完全解耦,并且能够自动封装结果到实体类

  1. 在mybatis-config.xml中增加配置,添加mapper配置,按照资源路径填写XML的路径,可以在加载配置的时候将xml加入资源中
    <mappers>
        <mapper resource="mapper/good.xml" />
    </mappers>

使用mybatis封装数据时,需要字段名称与实体类的属性名称相同,若字段是使用下划线分割单词时可以在配置文件中加入
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
将下划线命名转为驼峰命名

  1. 执行SQL语句

SqlSession提供了几种select方法

  • <T> T selectOne(String statement) :查询单一结果
  • <E> List<E> selectList(String statement); :查询结果集,返回一个List对象

示例:

package com.mybatis;

import com.mybatis.entity.Good;
import com.mybatis.utils.MyBatisUtils;
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.Test;

import java.io.IOException;
import java.io.Reader;
import java.sql.Connection;
import java.util.List;

public class TestMyBatis {
    @Test
    public void select() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.getSqlSession();
            List<Good> list = sqlSession.selectList("good.selectGoods");

            for (Good good : list)
                System.out.println(good);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MyBatisUtils.close(sqlSession);
        }
    }
}

SQL传参

如果想要查找指定数据或某些条件下的数据,这时我们就需要给SQL语句传递参数

    <select id="selectById" parameterType="Integer" resultType="com.mybatis.entity.Good">
        SELECT * FROM t_goods WHERE goods_id = #{value}
    </select>

parameterType指定传入参数的数据类型,Java中的数据类型都能够通用。在SQL语句中使用#{value}用于表示传入的参数,在Java中调用该语句时会自动将参数传递进来

    @Test
    public void testSelectById() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.getSqlSession();

            Good good = sqlSession.selectOne("good.selectById", 746);

            System.out.println(good);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MyBatisUtils.close(sqlSession);
        }
    }

在Java中调用<T> T selectOne(String statement, Object parameter)方法,第二个参数书写需要传递的参数,返回唯一对象

如果有多个参数需要传递给SQL,可以为参数类型指定为java.util.Map,这样就可以向SQL传递一个map集合,将需要用到的参数放到map集合中,在SQL语句中就可以自动解析map集合将对应键的值赋在指定位置

    <select id="selectByPrice" parameterType="java.util.Map" resultType="com.mybatis.entity.Good">
        SELECT * FROM t_goods WHERE current_price BETWEEN #{min} AND #{max} LIMIT #{limit}
    </select>
    @Test
    public void testSelectByPrice() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.getSqlSession();

            Map<String, Integer> map = new HashMap<>();
            map.put("min", 100);
            map.put("max", 500);
            map.put("limit", 10);

            List<Good> list = sqlSession.selectList("good.selectByPrice", map);
            for (Good good : list)
                System.out.println(good);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MyBatisUtils.close(sqlSession);
        }
    }

多表联查

在实际开发中,经常会遇到多表联查的情况,这时产生的结果集就不一定能够与实体类相对应。所以针对于这种情况可以将resultType指定为java.utils.Map即将查询结果封装为一个Map集合,数据库字段名为键,数据为值

    <select id="selectGoodsMap" resultType="java.util.Map">
        SELECT tg.*, tc.category_name
        FROM t_goods tg
        INNER JOIN t_category tc ON tg.category_id = tc.category_id
        LIMIT 10
    </select>
    @Test
    public void testSelectGoodsMap() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.getSqlSession();

            List<Map> list = sqlSession.selectList("good.selectGoodsMap");
            for (Map map : list)
                System.out.println(map);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MyBatisUtils.close(sqlSession);
        }
    }

如此输出的字段是乱序的,并不是按照SQL查询字段顺序,所以如果想要按实际查询顺序则需要使用LinkedHashMap

使用Map结果集来获取结果易于扩展也易于操作,但是却不利于进行编译时检查,对于SQL的查询结果没有对应的实体类来装载

ResultMap结果映射

针对于将结果集生成为map集合的不便,mybatis提供了ResultMap结果集将查询的字段和实体类的属性相互匹配,完成数据的封装。

一般的大型项目都会使用多表联查的方法获取结果,所以单独对应表结构的实体类无法满足对查询结果封装,因此还可以使用DTO类(Data Transfer Object)数据传输对象封装新的实体类

    <resultMap id="rmGoods" type="com.mybatis.dto.GoodDTO">
        <id property="good.goodsId" column="goods_id"/>
        <result property="good.title" column="title"/>
        <result property="good.subTitle" column="sub_title"/>
        <result property="good.originalCost" column="original_cost"/>
        <result property="good.currentPrice" column="current_price"/>
        <result property="good.discount" column="discount" />
        <result property="good.isFreeDelivery" column="is_free_delivery" />
        <result property="good.categoryId" column="category_id"/>
        <result property="category.categoryId" column="category_id" />
        <result property="category.categoryName" column="categoryName" />
        <result property="category.parentId" column="parent_id" />
        <result property="category.categoryLevel" column="category_level" />
        <result property="category.categoryOrder" column="category_order"/>

        <result property="test" column="test"/>
    </resultMap>

    <select id="selectGoodsDTO" resultMap="rmGoods">
        SELECT *, 1 AS test
        FROM t_goods tg
        INNER JOIN t_category tc ON tg.category_id = tc.category_id
        LIMIT 10
    </select>

resultMap标签中id为结果集的唯一标识,type为结果集所对应的实体类。

resultMap有两个子标签idresultid标签用于标识数据表的主键字段,其他字段使用result即可。两个子标签存在两个常用属性propertycolumnproperty表示实体类的属性,column表示数据表的字段名称。

INSERT

现在Mapper XML中创建insert标签,insert标签中常用两个属性id,表示标签的唯一标识,parameterType表示传入的参数类型

    <insert id="insert" parameterType="com.mybatis.entity.Good">
        INSERT INTO t_goods (title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)
        VALUES (#{title}, #{subTitle}, #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery}, #{categoryId})
    </insert>

使用#{property}来替代实体类的数据。

selectKeyuseGeneratedKeys

selectKeyuseGeneratedKeys都是用来获取数据集的主键信息并返回给对应的实体类

selectKey单独写在insert标签中,使用独立的SQL语句,该标签有以下几个属性

  • resultType:结果类型,可以使用Java中的各种数据类型
  • keyProperty:查询出的结果返回给实体类的属性
  • order:执行顺序,此处的执行顺序是指是否先完成插入语句,AFTER为执行插入后执行,BEFORE为执行插入前执行
    <insert id="insert" parameterType="com.mybatis.entity.Good" useGeneratedKeys="">
        INSERT INTO t_goods (title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)
        VALUES (#{title}, #{subTitle}, #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery}, #{categoryId})

        <selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">
            select last_insert_id()
        </selectKey>
    </insert>

selectKey适用于所有的关系数据库,应用范围广,但是使用却较为复杂

useGeneratedKeys是写在insert标签中的作为insert标签的属性出现,默认为false,将其设置为true则开启使用返回主键信息的功能。同时使用的还有keyPropertykeyColumn属性表示实体类的属性和对应数据表的字段

    <insert id="insert" parameterType="com.mybatis.entity.Good" 
            useGeneratedKeys="true" keyProperty="goodsId" keyColumn="goods_id">
        INSERT INTO t_goods (title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)
        VALUES (#{title}, #{subTitle}, #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery}, #{categoryId})
    </insert>

useGeneratedKeys只能用于拥有“自增主键”的数据库,如MySQL、SQL Server,而Oracle则无法使用该属性,如果需要主键只能使用selectKey标签

UPDATE

使用update标签书写SQL语句

<update id="update" parameterType="com.mybatis.entity.Good">
    UPDATE t_goods SET title = #{title},
                       sub_title = #{subTitle},
                       original_cost = #{originalCost},
                       current_price = #{currentPrice},
                       discount = #{discount},
                       is_free_delivery = #{isFreeDelivery},
                       category_id = #{categoryId}
    WHERE goods_id = #{goodsId}
</update>

再调用SqlSessionupdate(String statement, Object parameter)执行SQL脚本,返回值为int类型的数值,表示影响的行数

@Test
public void testUpdate() {
    SqlSession sqlSession = null;

    try {
        sqlSession = MyBatisUtils.getSqlSession();

        Good good = sqlSession.selectOne("good.selectById", 2669);

        good.setTitle("测试更新");
        int update = sqlSession.update("good.update", good);

        sqlSession.commit();

    } catch (Exception e) {
        if (sqlSession != null)
            sqlSession.rollback();

        e.printStackTrace();
    } finally {
        MyBatisUtils.close(sqlSession);
    }
}

一般修改数据时先获取对应的原始数据,再利用Setter方法进行修改数据

DELETE

使用delete标签书写删除的SQL语句

<delete id="delete" parameterType="Integer">
    DELETE FROM t_goods WHERE goods_id = #{value}
</delete>

再调用SqlSessiondelete(String statement, Object parameter)方法删除对应的数据,返回值为int类型的整数表示影响的行数

SQL动态查询

如上文描述的查询、插入、更新、删除操作的条件语句都是写死在SQL语句中,但是在实际开发过程中用户选择的条件是多变的,因此MyBatis提供了动态语句以解决这个问题

<select id="dynamicSelect" parameterType="java.util.Map" resultType="com.mybatis.entity.Good">
    SELECT * FROM t_goods
    <where>
        <if test="categoryId != null">
            AND category_id = #{categoryId}
        </if>
        <if test="currentPrice != null">
            AND current_price &lt; #{currentPrice}
        </if>
    </where>
</select>

where标签在mybatis中会自动生成where条件,所有的条件都由if标签进行判断,如果值存在则会添加该条件到where语句中。where标签会自动检测后边的条件并自动去掉and关键字,若所有的限制条件都不存在则不会再生成where关键字,保证SQL语法的正确性

@Test
public void testDynamicSelect() {
    SqlSession sqlSession = null;

    try {
        sqlSession = MyBatisUtils.getSqlSession();

        Map map = new HashMap();
//            map.put("categoryId", 739);
        map.put("currentPrice", 200);

        List<Good> list = sqlSession.selectList("good.dynamicSelect", map);
        for (Good good : list)
            System.out.println(good);

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        MyBatisUtils.close(sqlSession);
    }
}

测试用例的输出结果如下:
在这里插入图片描述

SQL恶意注入的问题

与JDBC类似,如果在前台的输入框中向后台传递一些带有关键字的信息时,会恶意的破坏SQL语句的基本逻辑,使得实现越权访问信息。

mybatis提供了两种方式进行参数传递

  • #{value}:会将参数转换的字符串带入SQL语句中可以避免被SQL注入
  • ${value}:该方法为原文本的传递参数存在SQL注入的风险

${}方法传递参数主要是用在后台进行对SQL语句的控制,对于前台输入的参数一般还是使用#{}方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值