首页 > 其他分享 >MyBatis

MyBatis

时间:2023-04-05 22:34:09浏览次数:27  
标签:name title user SQL MyBatis where id

MyBatis

MyBatis是一款持久层框架,用于简化JDBC开发。

  • 持久层

    • 负责将数据保存到数据库的代码
    • JavaEE三层架构:表现层、业务层、持久层
  • 框架

    • 框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型
    • 在框架的基础上建构软件编写更加高效、规范、通用、可拓展
  • JDBC缺点

    • 硬编码

      注册驱动,获取连接

      SQL语句

    • 操作繁琐

      手动设置参数

      手动封装结果集

  • MyBatis简化

    • 硬编码-->配置文件
    • 操作繁琐-->自动完成

    MyBatis几乎免除了所有的JDBC代码以及设置参数和获取结果集的工作

MyBatis快速入门

编写MyBatis核心配置文件 --> 替换连接信息 解决硬编码问题

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/test?useServerPrepStmts=true"/>
                <property name="username" value="root"/>
                <property name="password" value="122381"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
<!--        加载sql映射文件-->
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

编写SQL映射文件 --> 统一管理sql语句,解决硬编码问题

编码

  • 加载核心配置文件,获取SqlSessionFactory对象

    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
  • 获取SqlSession对象,执行SQL语句

    SqlSession session = sqlSessionFactory.openSession();
    //参数是SQL映射问件的名称空间.id
    List<User> users = session.selectList("user.selectAll");
    
  • 释放资源

    session.close();
    

Mapper代理开发

目的

  • 解决原生方式中的硬编码问题

    如:

    List<User> users = session.selectList("user.selectAll");
    

    更改为:

    //获取接口代理对象
    UserMapper userMapper = session.getMapper(UserMapper.class);
    //执行方法,也就是执行sql语句
    List<User> users = userMapper.selectAll();
    
  • 简化后期执行SQL

使用Mapper代理开发步骤

  • 定义与SQL映射文件同名的Mapper接口,并将Mapper接口和SQL映射文件放置在同一目录下
  • 设置SQL映射文件的namespace属性为Mapper接口全类名
  • 在Mapper接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致

细节:如果Mapper接口的名称和SQL映射文件名称相同,并在同一目录下,可以使用包扫描的方式简化SQL映射文件的加载

<!--        MyBatis核心配置文件-->
<mappers>
<!--        加载sql映射文件-->
<!--        <mapper resource="UserMapper.xml"/>-->
<!--        如果Mapper接口的名称和SQL映射文件名称相同,并在同一目录下,可以使用包扫描的方式简化SQL映射文件的加载-->
    <package name="mapper"/>
</mappers>

MyBatis核心配置文件

配置各个标签时,需要注意约束的先后顺序

environments

配置数据库连接环境信息,可以配置多个environment,通过default属性切换不同的environment

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/test?useServerPrepStmts=true"/>
            <property name="username" value="root"/>
            <property name="password" value="122381"/>
        </dataSource>
    </environment>
</environments>

transactionManager与dataSource均会被spring管理

typeAliases

<typeAliases>
<!--        类型别名,在SQL映射文件中,resultType对指定包下的类可以直接用不区分大小写的类名代替全类名-->
    <package name="domain"/>
</typeAliases>

配置文件完成增删改查

查询

查询所有数据

List<User> selectAll();
<select id="selectAll" resultType="User">
    select * from user
</select>

<!--    数据库的字段名称和实体类字段名称不一样时,不能自动封装-->

<!--    可以通过起别名来解决-->
<select id="selectAll" resultType="User">
    select name as name, password as password from user
</select>

<!--    也可以通过定义、引用sql片段来解决-->
<sql id="user_column">
    name as name, password as password
</sql>
<select id="selectAll" resultType="User">
    select <include refid="user_column"></include> from user
</select>

<!--    通过resultMap解决-->
<!--    id: 唯一标识    type: 映射的类型-->
<resultMap id="UserResultMap" type="User">
<!--        id: 用于主键字段的映射-->
<!--        result: 用于普通字段的映射-->
<!--        column: 表的列名    property: 实体类的名称-->
    <result column="name" property="name"></result>
    <result column="password" property="password"></result>
</resultMap>
<select id="selectAll" resultMap="UserResultMap">
    select * from user
</select>

查询某条数据

User selectByName(String name);
<!--    #{}:执行SQL时,会将其替换为?,将来自动设置参数-->
<!--    ${}:拼接SQL,会出现SQL注入问题-->
<!--    参数类型:parameterType可以省略-->
<select id="selectByName" parameterType="string" resultType="User">
    select * from user where name = #{name}
</select>

<!--    特殊字符:转义字符;CDATA区-->
<select id="selectById" resultType="User">
    select * from user where id &lt; #{id}
</select>
<select id="selectById" resultType="User">
    select * from user where id 
    <![CDATA[
		<
    ]]>
    #{id}
</select>

条件查询

//    散装参数:如果方法中有多个参数,需要使用@Param("SQL参数占位符名称")
List<User> selectByCondition(@Param("name") String name, @Param("password") String password);

//    对象参数:对象属性名称要与SQL参数占位符名称一致
List<User> selectByCondition(User user);

//    Map参数:Map集合的键的名称要与SQL参数占位符名称一致,值就是想要传递的参数值
List<User> selectByCondition(Map map);
<select id="selectByCondition" resultType="User">
    select * from user where name = #{name} and password = #{password}
</select>

动态查询

List<User> selectDynamic(User user);
<select id="selectDynamic" resultType="User">
    select * from user
    <where>
        <if test="id != null and id != 0">
            and id = #{id}
        </if>
        <if test="name != null">
            and name = #{name}
        </if>
        <if test="password != null">
            and password = #{password}
        </if>
    </where>
</select>
List<User> selectUserIn(List<String> users);
<select id="selectUserIn" resultType="User">
    select * from user where
    <choose>
        <when test="list.isEmpty()">
            1 = 0
        </when>
        <otherwise>
            name in
            <foreach item="item" index="index" collection="list"
                     open="(" separator="," close=")" nullable="true">
                #{item}
            </foreach>
        </otherwise>
    </choose>
</select>

MyBatis事务

openSession()	//默认开启事务,进行增删改操作后,要使用session.commit()手动提交事务
openSession(true)	//设置为自动提交(关闭事务)

增加

void add(User user);
<!--    useGeneratedKeys和keyProperty属性,将添加数据后,数据库自动生成的id返回并设置到传入对象的指定字段-->
<insert id="add" useGeneratedKeys="true" keyProperty="id">
    insert into user (name, password) values (#{name}, #{password})
</insert>

修改

修改全部字段

void update(User user);
<update id="update">
    update user set name = #{name}, password = #{password}
    where id = #{id}
</update>

修改动态字段

void updateDynamic(User user);
<update id="updateDynamic">
    update user
    <set>
        <if test="name != null">
            name = #{name},
        </if>
        <if test="password != null">
            password = #{password},
        </if>
    </set>
    where id = #{id}
</update>

删除

删除一个

void deleteById();
<delete id="deleteById">
    delete from user where id = #{id};
</delete>

批量删除

void deleteByIds(List<Integer> ids);
<delete id="deleteByIds">
    delete from user where
    <choose>
        <when test="list.isEmpty()">
            1 = 0
        </when>
        <otherwise>
            <foreach item="id" index="index" collection="list"
            open="(" close=")">
                #{id}
            </foreach>
        </otherwise>
    </choose>
</delete>

动态SQL

SQL语句会随着用户的输入或外部条件的变化而变化,称为动态SQL

if
choose (when, otherwise)
trim (where, set)
foreach

以下样内容来自官方文档

if

使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:

<select id="findActiveBlogWithTitleLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

这条语句提供了可选的查找文本功能。如果不传入 “title”,那么所有处于 “ACTIVE” 状态的 BLOG 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果(细心的读者可能会发现,“title” 的参数值需要包含查找掩码或通配符字符)。

如果希望通过 “title” 和 “author” 两个参数进行可选搜索该怎么办呢?首先,我想先将语句名称修改成更名副其实的名称;接下来,只需要加入另一个条件即可。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

choose、when、otherwise

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG(这可能是管理员认为,与其返回大量的无意义随机 Blog,还不如返回一些由管理员精选的 Blog)。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

trim、where、set

前面几个例子已经方便地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 “state = ‘ACTIVE’” 设置成动态条件,看看会发生什么。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE
  <if test="state != null">
    state = #{state}
  </if>
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:

SELECT * FROM BLOG
WHERE

这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:

SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’

这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。

MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

或者,你可以通过使用trim元素来达到同样的效果:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

注意,我们覆盖了后缀值设置,并且自定义了前缀值。

foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  <where>
    <foreach item="item" index="index" collection="list"
        open="ID in (" separator="," close=")" nullable="true">
          #{item}
    </foreach>
  </where>
</select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

上例中,若传入的可迭代对象中元素个数为0,相当于执行select * from post p,返回全部元素

因此,要在程序中提前判断,若要查询的集合为空即可不用执行查询

MyBatis参数传递

单个参数

  • POJO类型

    直接使用,保证属性名和参数占位符名称一致

  • Map集合

    直接使用,保证Map集合的键名和参数占位符名称一致

  • Collection

    map.put("arg0", collection集合)
    map.put("collection", collection集合)
    
  • List

    map.put("arg0", list集合)
    map.put("collection", list集合)
    map.put("list", list集合)
    
  • Arraay

    map.put("arg0", 数组)
    map.put("array", 数组)
    
  • 其他类型

多个参数

封装为Map集合

map.put("arg0", 参数值1)
map.put("param1", 参数值1)
……

注解完成增删改查

使用注解完成简单功能

配置文件完成复杂功能

@Select("select * from user where id = #{id}")
User selectById(int id);

标签:name,title,user,SQL,MyBatis,where,id
From: https://www.cnblogs.com/Lerjiu/p/17291159.html

相关文章

  • ShardingSphereJDBC+MybatisPlus实现分库分表
    前言这篇是ShardingSphere-JDBC+Springboot+MybatisPlus+Druid分库分表的简单例子,我们用一个订单表为例,通过简单配置实现数据分片到多个数据库的多个表中。主要配置和代码已经在文中给出,完整例子可以参考GitHub-fruitbasket-litchi-shardingjdbc。准备数据库在一个或两个My......
  • 模拟mybatis接口动态注册过程
    思考  前文提到2种方式动态加载bean到容器,这里我们模拟一下mybatis手动注册Bean到容器的过程。模拟有啥好处,主要是为了方便你理解如何手动注册Bean的过程及mybatis的设计思想。毕竟这个才是spring的核心内容。    首先思考一下问题    如果你实现这个问题,你准备怎......
  • MyBatis-Plus 写 Join 联表查询
    效果展示背景众所周知,MybatisPlus封装的mapper不支持join,如果需要支持就必须自己去实现。但是对于大部分的业务场景来说,都需要多表join,要不然就没必要采用关系型数据库了。使用方法仓库地址https://gitee.com/best_handsome/mybatis-plus-join安装Maven<depend......
  • MyBatis的执行流程及核心组件
    MyBatis的执行流程及核心组件如图所示。基本组件介绍Configuration用于描述MyBatis的主配置信息,其他组件需要获取配置信息时,直接通过Configuration对象获取。除此之外,MyBatis在应用启动时,将Mapper配置信息、类型别名、TypeHandler等注册到Configuration组件中,其他组件需要这......
  • MyBatisPlus——条件查询——null值处理与查询投影
    DQL编程控制条件查询——设置查询条件格式一:常规格式 //方式一:按条件查询 QueryWrapperqw=newQueryWrapper(); //10岁到30岁之间的用户 qw.gt("age",10); qw.lt("age",30); List<User>users=userDao.selectList(qw); System.out.println(users);格式......
  • MyBatis插件原理解析(转载)
    前言大多数框架都支持插件,用户可通过编写插件来自行扩展功能,Mybatis也不例外。在Mybatis中最出名的就是PageHelper分页插件,下面我们先来使用一下这个分页插件。如何集成分页插件Spring-Boot+Mybatis+PageHelper引入pom依赖<dependency><groupId>com.github.pagehelper<......
  • 如何通过MyBatis的插件功能来实现分表功能
    前言之前项目中由于几张表的数据量特别的大,在查询的时候特别耗时,所以决定对其进行分表处理。考虑到项目中用的MyBatis作为ORM框架,所以便决定使用它的插件功能来实现动态的替换查询的表来实现分表功能。功能实现整体的目录结构如下TableShard:注解,用来标记需要分表的接口或者......
  • mybatis调用存储过程,并返回out参数
        ......
  • Mybatis-Plus代码生产器
    【问题描述】在微服务的开发过程中,每次新创建一个微服务项目的时候,都需要手工添加controller、service、entity和mapper文件夹和文件时会比较麻烦,并且增加不少的工作量。是否有一个模块,可以自动生成对应的目录和文件呢?真的有这样的代码生产器文件:一、使用之前先确认POM文件中下......
  • myBatis报错org.apache.ibatis.ognl.NoSuchPropertyException
    跑批任务时mybatis报错org.apache.ibatis.ognl.NoSuchPropertyException,重跑未出现报错,百度发现是由于mybatis依赖的Ognl版本OgnlRuntime.getMethodValue在并发情况下会存在并发问题,错误地返回null引起报错 以下是搜索该问题时找到的资料:https://github.com/1993hzh/tho......