Mybatis基础

Mybatis基础

什么是MyBatis?

  • MyBatis是一款优秀的持久层框架,用于简化JDBC开发
  • MyBatis负责将数据保存到数据库的那一层代码
  • JavaEE三层架构:表现层,业务层,持久层

JDBC缺点

image-20230707120843596

MyBatis对JDBC的优化

image-20230707121353999

原生MyBatis快速入门

**例子:**查询User表中的所有数据

  1. 创建一个User表,添加数据
  2. Maven创建模块,导入坐标
  3. 编写MyBatis 核心配置文件 → \to 替换数据库连接信息【解决硬编码问题】
  4. 编写SQL映射文件 → \to 统一管理SQL语句【解决硬编码问题】
  5. 编码:
    1. 定义实体POJO类
    2. 加载核心配置文件,获取SqlSessionFactory对象
    3. 获取SqlSession对象,执行SQL语句
    4. 释放资源

创建一个mybatis-config.xml配置文件

<?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.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--加载sql的映射文件-->
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

再创建一个SQL的Mapper映射文件【UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
    namespace:名称空间,将不同的Mapper像包一样区分开来
-->
<mapper namespace="test">
    <select id="selectAll" resultType="com.itheima.pojo.User">
        select * from tb_user;
    </select>
</mapper>

主函数加载配置文件获取SqlSessionFactory并创建SqlSession对象执行sql

public static void main(String[] args) throws IOException {
        System.out.println("MyBatis快速入门程序案例...");

        //1. 加载MyBatis的核心配置文件,获取SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //2. 获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //3. 执行SQL语句,调用SqlSession对象的方法,并且将Mapper映射文件的命名空间+方法ID
        List<User> users = sqlSession.selectList("test.selectAll");
        System.out.println(users);

        //4. 释放资源
        sqlSession.close();
    }

Mapper代理开发

目的

  • 解决原生方式中的硬编码【需要依赖字符串的字面值】

    //原生的方法
    List<User> users = sqlSession.selectList("test.selectAll");
    System.out.println(users);
    //使用Mapper代理之后的方法:
    //1. 获取接口代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //2. 执行方法,调用SQL语句
    List<User> users = userMapper.selectAll();
    
  • 简化后期执行SQL

操作实现

  1. 定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下
  2. 设置SQL映射文件的namespace属性为Mapper接口的全限名
  3. 在Mapper接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致
  4. 编码
    1. 通过SqlSession的getMapper方法获取Mapper接口的代理对象
    2. 调用对应方法完成sql的执行

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

核心配置文件

在配置文件mybatis-config.xml文件中:配置文件包含了会深深影响MyBatis行为的设置和属性信息。配置文档的结构如下:

  • configuration(配置)
    • properties(属性)
    • settings(设置)
    • typeAliases(类型别名)
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境配置)
      • envirment(环境变量)
        • transactionManager(事务管理器)【一般不用修改】
        • dataSource(数据源)【一般不用修改】
    • databaseldProvider(数据库厂商表示)
    • mappers(映射器)
  1. Mapper属性:用于加载SQL的映射文件

        <mappers>
            <!--加载sql的映射文件-->
    		<!--<mapper resource="com/itheima/mapper/UserMapper.xml"/>-->
            <package name="com.itheima.mapper"/>
        </mappers>
    
  2. environment属性:配置数据库连接环境信息,可以配置多个environment信息,通过default属性切换不同的environment

    <environments default="development">
    <environment id="development">
        <!--数据库事务管理的配置-->
    	<transactionManager type="JDBC"/>
        	<!--数据库连接池的配置-->
    		<dataSource type="POOLED">
    		<!--数据库连接信息-->
    			<property name="driver" value="com.mysql.jdbc.Driver"/>
    			<property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
    			<property name="username" value="root"/>
    			<property name="password" value="123456"/>
    		</dataSource>
    	</environment>
    </environments>
    
  3. typeAliases属性:配置类型别名

    <!-- 在没有配置类型别名的时候,写resultType的时候经常要写一大堆包名+类名 -->
    <select id="selectAll" resultType="com.itheima.pojo.User">
            select * from tb_user;
    </select>
    

    mybatis-config.xml配置文件中设置别名之后,会扫描别名配置中的包下的所有类,并起别名【别名默认为类型不区分大小写】

    <!-- 配置别名,自动扫描包下的所有实体类 -->
    <typeAliases>
    	<package name="com.itheima.pojo"/>
    </typeAliases>
    <!-- 配置别名之后,resulttype可以用别名来书写 -->
    <select id="selectAll" resultType="user">
            select * from tb_user;
    </select>
    

    细节:配置各个标签时,需要遵循前后顺序

Mapper代理开发快速入门

  1. 创建mybatis-config.xml文件配置数据库信息

    <?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>
        
    <typeAliases>
    <package name="com.itheima.pojo"/>
    </typeAliases>
        
    	<environments default="development">
    		<environment id="development">
    			<transactionManager type="JDBC"/>
    			<dataSource type="POOLED">
    				<!--数据库连接信息-->
    				<property name="driver" value="com.mysql.jdbc.Driver"/>
    				<property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
    				<property name="username" value="root"/>
    				<property name="password" value="123456"/>
    			</dataSource>
    		</environment>
    	</environments>
    	<mappers>
    	<!--加载sql的映射文件-->
    	<!--<mapper resource="com/itheima/mapper/UserMapper.xml"/>-->
    		<package name="com.itheima.mapper"/>
    	</mappers>
    </configuration>
    
  2. 创建一个Mapper接口文件【此处文件为UserMapper

    package com.itheima.mapper;
    import com.itheima.pojo.User;
    import java.util.List;
    
    public interface UserMapper {
        //写入接口需要实现的方法
        List<User> selectAll();
    }
    
  3. 创建一个SQL映射文件【文件名:UserMapper.xml】注:映射文件与接口文件处于同一目录下

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.itheima.mapper.UserMapper">
        <select id="selectAll" resultType="user">
            select * from tb_user;
        </select>
    </mapper>
    
  4. 主函数实现:

    package com.itheima;
    
    import com.itheima.mapper.UserMapper;
    import com.itheima.pojo.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 java.io.IOException;
    import java.io.InputStream;
    import java.util.List;
    
    public class MybatisDemo {
        public static void main(String[] args) throws IOException {
            //1. 加载MyBatis的核心配置文件,获取SqlSessionFactory
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
            //2. 获取SqlSession对象
            SqlSession sqlSession = sqlSessionFactory.openSession();
    
            //3.1  获取UserMapper接口的代理对象
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            List<User> users = userMapper.selectAll();
            System.out.println(users);
            //4. 释放资源
            sqlSession.close();
        }
    }
    

查询数据&结果映射

数据库表的字段名称实体类的属性名称不一样时,则不能自动封装数据

  • 解决方法

    1. 起别名:对不一样的列名起别名,让别名和实体类的属性名一样

      • 缺点:每次查询都要重新定义一次别名
      <!--
      一般的查询的时候,若是数据库字段名称和Java实体类属性名称不一致的时候就无法自动封装 
      -->
      select * from tb_brand;
      <!--
      比如在数据库中,列名有brand_name,company_name而在Java实体类中属性为brandName和companyName此时就要别名:
      -->
      <select id="selectAll" resultType="brand">
      	select id,brand_name as brandName,company_name as companyName, ordered,description,status
          from tb_brand;
      </select>
      

      当需要多次查询同样的属性列的时候,可以使用SQL片段:

      <sql id="all_tabs">
          id,brand_name as brandName,company_name as companyName,ordered,description,status
      </sql>
      
      <select id="selectAll" resultType="brand">
          select 
          	<include refid="all_tabs" />
          from tb_brand;
      </select>
      
    2. 使用resultMap

      • 定义<resultMap>标签
      • <select>标签中,使用resultMap属性替换 resultType属性
      <resultMap id="brandResultMap" type="brand">
      	<!--
       		id:完成主键字段的映射
      			column:表的列名
      			property:实体类的属性名
      		result:完成一般字段的映射
      	-->
          <result column="brand_name" property="brandName"/>
      	<result column="company_name" property="companyName"/>
      </resultMap>
      
      <select id="selectAll" resultMap="brandResultMap">
      	select * from tb_brand;
      </select>
      

案例一:查询详细信息

  1. 编写接口方法:Mapper接口

    Brand selectById(int id);
    
    • 参数:id

    • 结果:Brand

  2. 编写SQL语句:SQL映射文件

    <select id="selectById" parameterType="int" resultType="brand">
    	select * from tb_brand where id = #{id};
    </select>
    

使用细节:

  • 参数占位符:

    1. #{}:会将其替换为 ? ,为了防止SQL注入

    2. ${}:拼接SQL,存在SQL注入问题

      • 使用时机:参数传递的时候使用 #{}
      • 表名和列名不固定的时候使用:${}
    3. 参数类型:parameterType:可以省略

      <select id="selectById" parameterType="int" resultMao="brandResultType">
      	select * 
          from tb_brand where id = #{id};
      </select>
      
    4. 特殊字符处理:

      • 转义字符:【转义字符比较少的时候建议使用】

        比如在SQL语句:
        select * from 表 where 参数1 < 参数2
        中,在XML配置文件中小于号是不允许出现的因此将上述SQL语句的小于号使用转义字符
        select * from 表 where 参数1 &lt 参数2
        
      • CDATA区域:【转义字符比较多的时候建议使用】

        CDATA区域的字符默认转义,因此SQL语句:
        select * from 表 where 参数1 
        <![CDATA[
        	<
        ]]>
        参数2
        

案例二:条件查询

SQL语句设置多个参数有几种方式?

  • 参数接收:
    1. 散装参数:需要使用@Param("SQL中的参数占位符名称")
    2. 实体类封装参数:只需要保证SQL中参数名和实体类属性名对应上,即可设置成功
    3. map集合:只需要保证SQL中的参数名和map集合的键的名称对应上,即可设置成功
多条件查询

image-20230710223539551

  1. 编写接口方法:Mapper接口

    // 第一种写法:
    List<Brand> selectByCondition(@Param("status")int status,
                                  @Param("companyName")String companyName,
                                  @Param("brandName")String brandName);
    // 第二种写法:
    List<Brand> selectByCondition(Brand brand);
    // 第三种写法:
    List<Brand> selectByCondition(Map map);
    
    • 参数:所有查询的条件【status、company_name、brand_name】

    • 结果:List

  2. 编写SQL语句:SQL映射文件【相当于JDBC的Statement

    <select id="selectByCondition" resultMap="brandResultMap">
    	select * 
        from tb_brand
        where 
        	status = #{status}
        	and company_name like #{companyName}
        	and brand_name like #{brandName}
    </select>
    

多条件查询的缺点:一定要对每一个参数占位符都要设置值,否知参数值为null

如何改进?动态SQL

案例三:动态条件查询

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

    <select id="selectByCondition" resultMap="brandResultMapr">
    	select * 
        from tb_brand
        where 
        	status = #{status}
        	and company_name = #{companyName}
        	and brand_name = #{brandName}
    </select>
    
  • MyBatis对动态SQL有很强大的支持:【提供了如下的标签,方便动态SQL的实现】

    1. if:用于判断参数是否有值,使用test属性进行条件判断
      • 存在的问题:第一个条件不需要逻辑运算符
      • 解决方案:
        1. 使用恒等式让所有条件格式都一样
        2. <where>标签替换where关键字
    2. choose(when,otherwise)
    3. trim(where,set)
    4. foreach

对案例二的条件查询进行优化

<select id="selectByCondition" resultMap="brandResultMapr">
	select * 
    from tb_brand
    <where>
    	<if test="status != null">
    		status = #{status}
    	</if>
		<if test="companyName != null and companyName != ''">
    		and company_name like #{companyName}
    	</if>
    	<if test="brandName != null and brandName != ''">
			and brand_name like #{brandName}
    	</if>
	</where>
</select>
查询-单条件动态查询
  • 从多个条件中选择一个
  • choose(when,otherwise):选择,类似Java中的switch语句
<select id="selectByConditionSingle" resultType="brandResultMap">
	select * 
    from tb_brand
    where 
    <choose>		<!-- 类似switch语句 -->
        <when test="status != null">		<!-- 类似case语句 -->
        	status = #{status}
        </when>
        <when test="companyName != null and companyName !='' ">
        	company_name like #{companyName}
        </when>
        <when test="brandName != null and brandName != '' ">
            brand_name like #{brandName}
        </when>
        <otherwise>		<!-- 类似Default语句 -->
        	1 = 1 
        </otherwise>
    </choose>
</select>

案例四:添加

  1. 编写接口方法:Mapper接口

    void add(Brand brand)
    
  2. 参数:处理id之外的所有数据

  3. 结果:void

  4. 编写SQL语句:SQL映射文件

    <insert id="add">
        insert into tb_brand(brand_name,company_name,ordered,description,status)
        values (#{brandName},#{companyName},#{orderd},#{description},#{status});
    </insert>
    

注意:

进行插入操作之后,需要手动提交事务…

sqlSession.commit()

MyBatis事务
  • openSession():默认开启事务,进行增删改查操作后需要使用 sqlSession.commit()手动提交事务
  • openSession(true):可以设置为自动提交事务(关闭事务)
添加-主键返回

数据添加成功之后,需要获取插入数据库的数据的主键的值

举个例子:

useGeneratedKeys=true设置生成主键值

keyProperty="属性名"将生成的主键值赋予该属性上

<insert id="addOrder" useGeneratedKeys="true" keyProperty="id">
	insert into tb_order(payment,payment_type,status)
    values (#{payment},#{paymentType},#{status});
</insert>

案例五:修改

<update id="updateInfo">
	update tb_user
		set username=#{username},
		password = #{password},
		gender = #{gender},
		addr = #{addr}
		where id = #{id};
</update>
动态字段的修改
<update id="updateInfo">
	update tb_user
		<set>
            <if test="username != null and username != '' ">
	            username=#{username},
			</if>
            <if test="password != null and password != '' ">
				password = #{password},
            </if>
            <if test="gender != null and gender != '' ">
				gender = #{gender},
            </if>
            <if test="addr != null and addr != '' ">
				addr = #{addr}
            </if>
		</set> 
		where id = #{id};
</update>

案例六:删除

删除单个
  1. 编写接口方法:Mapper接口

    void deleteById(int id);
    
  2. 参数:处理id之外的所有数据

  3. 结果:void

  4. 编写SQL语句:SQL映射文件

    <delete id="deleteById">
        delete from tb_brand where id=#{id};
    </delete>
    
批量删除
  1. 编写接口方法:Mapper接口

    void deleteByIds(@Params("ids")int[] ids);
    
    • 参数:id数组
    • 结果:void
  2. 编写SQL语句:SQL映射文件

    <delete id="deleteByIds">
    	delete from tb_brand
        where id in 
        <foreach collection="ids" item="id" separator=","open="(" close=")">
            #{id}
        </foreach>
    </delete>
    

    细节注意:

    mybatis会将数组参数,封装为一个Map集合

    • 默认:array = 数组

      <!--比如,在mapper接口中写如下接口 -->
      int deleteByIds(int[] ids);
      <!--则在XML映射文件中要如下书写 -->
      <foreach collection="array" item="id" separator=","open="(" close=")">
      	#{id}
      </foreach>
      
    • 使用@Param注解改变map集合的默认key的名称

      <!--使用@Param注解后,在mapper接口中写如下接口 -->
      int deleteByIds(@Param("ids")int[] ids);
      <!--则在XML映射文件中要如下书写 -->
      <foreach collection="ids" item="id" separator=","open="(" close=")">
              #{id}
      </foreach>
      

参数传递

MyBatis提供了ParamNameResolver 类来进行参数封装

MyBatis接口方法中可以接收各种各样的参数,MyBatis底层对于这些参数进行不同的封装处理方式

单个参数:

  1. POJO类型:直接使用,实体类属性名 和 参数占位符名称一致

  2. Map集合:直接使用,键名 和 参数占位符名称 一致

  3. Collection:封装为Map集合

    map.put("collection",collection集合);
    map.put("arg0",collection集合)
  4. List:封装为Map集合

    map.put("collection",list集合);
    map.put("list",list集合);
    map.put("arg0",list集合);
    
  5. Array:封装为Map集合

    map.put("array",数组);
    map.put("arg0",数组);
    
  6. 其他类型:直接使用

多个参数:封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名

不设置@Param的时候,传递参数之后底层实现为:

// 1. 比如在下述接口中传递两个参数并且不使用@Param注解
User addInfo(String username,String password);
// 2. 参数1为username,参数2为password,则Mybatis底层创建map集合并
map.put("arg0",参数值1);
map.put("param1",参数值1);
map.put("arg1",参数值2);
map.put("param2",参数值2);

// 1. 使用@Param之后,接口如下
User addInfo(@Param("名1")String username,@Param("名2")String password);
// 2. 参数1为username,参数2为password,则Mybatis底层创建map集合并
map.put("名1",参数值1);
map.put("param1",参数值1);
map.put("名2",参数值2);
map.put("param2",参数值2);

注解开发

使用注解开发会比配置文件开发更加方便

@Select("select * from tb_user where id = #{id}")
public User selectById(int id);
  • 查询:@Select
  • 添加:@Insert
  • 修改:@Update
  • 删除:@Delete

注解完成简单功能

配置文件完成复杂功能

  • 使用注解来映射简单语句会使代码显的更加简洁,但对于稍微复杂一点的语句,Java注解就力不从心,还会让本就复杂的SQL语句更加混乱不堪。因此,当需要完成复杂的操作,最好使用XML来映射语句
  • 选择何种方式来配置映射,以及认为是否要统一映射语句的定义形式取决于实际需求。因此可以自由的在基于注解和XML的语句映射方式间移植和切换
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_橙留香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值