MyBatis

什么是MyBatis?

MyBatis是一款优秀的持久层1框架2,用于简化JDBC开发。MyBatis本是Apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github
官网:https://mybatis.org/mybatis-3/

mybatis简化jdbc:MyBatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qeXR5ccb-1721137208680)(https://i-blog.csdnimg.cn/direct/3187e85170c54319b80c31d061d1c7ed.png#pic_center)]

一、mybatis快速入门

需求:查询emp表中所有数据

1.创建emp表,添加数据

2.创建模块,***pom.xml***文件中导入坐标

<!--mybatis-->
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>3.5.7</version>
</dependency>

<!--mysql-->
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>8.0.33</version>
</dependency>

<!--junit-->
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.13.1</version>
	<scope>test</scope>
</dependency>

3.编写mybatis核心配置文件mybatis-config.xml----》替换连接信息 解决硬编码问题

  • 之前连接信息是写在Java代码中的,现在改到配置文件里去,可以解决硬编码问题。
<?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:///javaweb02"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!--添加sql映射文件,此时核心配置文件与sql映射文件在同一文件夹下-->
        <mapper resource="EmpMapper.xml"/>
    </mappers>
</configuration>

4.编写sql映射文件EmpMapper.xml —》 统一管理sql语句,解决硬编码问题

<?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 namespace="EmpMapper">
    <!--id:此sql语句唯一标识    resultType:全类名,实体类-->
    <select id="findAllEmp" resultType="org.example.pojo.Emp">
        select * from emp
    </select>
</mapper>

5.编码

  • 1.定义pojo类,根据数据库中emp表创建出对应的Emp实体类
  • 2.创建mybatis测试类,加载核心配置文件,获取SqlSessionFactory对象
  • 3.通过SqlSessionFactory获取SqlSession对象,执行sql语句,
  • 4.释放资源
public class EmpTest {
    public static void main(String[] args) throws IOException {
        // 2.加载核心配置文件,获取SqlSessionFactory对象
        String resource = "mybatis-config.xml";
        // 使用mybatis中的工具类Resources
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 创建sqlSessionFactory工厂类对象,
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 使用工厂类对象sqlSessionFactory创建sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 3.使用sqlSession对象,调用selectList()方法,执行sql语句,
        // 传入sql语句的唯一标识(名称空间+id)
        List<Object> emps = sqlSession.selectList("EmpMapper.findAllEmp");
        //selectList()方法中的参数也属于硬编码,
        //想要解决这个问题,请接着看Mapper代理👇
        //遍历打印
        emps.forEach(emp -> System.out.println(emp));
        // 4.释放资源
        sqlSession.close();
    }
}

二、Mapper代理开发

目的:解决原生方式中的硬编码,简化后期执行sql。 mybatisX插件

使用Mapper代理方式完成入门案例

1.定义与sql映射文件同名的Mapper***接口***,并且将Mapper接口和SQL映射文件放置在同一目录下(编译后)

  • 使用maven进行编译后我们发现,在target/classes中存在xml文件,但是要想将mapper接口与sql映射文件放在同一文件夹下需要更改sql映射文件的放置路径,这样的话在编译完成后Mapper接口和sql映射文件将会放到同一文件夹下。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rNgxjSec-1721137208688)(https://i-blog.csdnimg.cn/direct/94cc3ed3f731489a8151149f634d8022.png#pic_center)]

  • 注意:EmpMapper应为接口

Q:mapper接口和sql映射文件为什么要放在同一文件夹下?

A:Mapper接口和SQL映射文件通常放在同一文件夹下的原因是为了方便MyBatis框架的自动扫描和配置。在MyBatis中,可以通过配置来告诉框架在哪里查找Mapper接口及其对应的XML映射文件。如果这些文件位于同一目录下,并且接口的名称与XML文件的名称相匹配,MyBatis可以自动关联它们,无需额外的配置。这样做可以提高开发效率,减少配置错误,并使得项目结构更加清晰

2.设置SQL映射文件的namespace属性的值为Mapper接口全限定名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Kipo5u5-1721137208691)(https://i-blog.csdnimg.cn/direct/5011cc02657a416e99fed944b5525dd0.png#pic_center)]

3.在Mapper接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持resultType参数类型和方法的返回值类型一致

public interface EmpMapper {
    public List<Emp> findAllEmp();
}

4.编码

  • 1.通过SqlSession的getMapper方法获取Mapper接口的代理对象
  • 2.调用对应方法完成sql的执行
@Test
public void testMapper() throws IOException {
    //1.使用自定义工具类MybatisUtils,获取sqlSession对象
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    //2.使用sqlSession获取mapper代理对象
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    //3.使用mapper代理对象调用接口方法,执行sql
    List<Emp> allEmp = mapper.findAllEmp();
    //遍历输出
    allEmp.forEach(emp -> System.out.println(emp));
    //4.关闭sqlSession
    sqlSession.close();
}

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

<mappers>
    <!--1.sql映射文件,映射单个文件-->
    <!--<mapper resource="org/example/mapper/EmpMapper.xml"/>-->
    
    <!--这两种方式使用其中一个-->
    
    <!--2.Mapper代理方式,映射整个包下的所有文件-->
    <package name="org.example.mapper"/>
</mappers>

三、MyBatis核心配置文件

需要严格按照mybatis官方给出的顺序对核心配置文件进行配置

https://mybatis.p2hp.com/configuration.html

四、配置文件完成增删改查

4.1、查询brand表,准备Brand实体类,创建BrandMapper接口,创建BrandMapper.xml

4.1.1查询所有数据

  • 1.编写接口方法:findAllBrand();方法
    • 参数:无
    • 结果:List<Brand>
  • 2.编写SQL语句:SQL映射文件
  • 3.执行方法,测试类:BrandMapperTest
List<Brand> allBrand = mapper.findAllBrand();

解决表中字段名和实体类中属性名称不一致的情况:

1.sql映射文件中,起别名

<select id="findAllBrand" resultType="org.example.pojo.Brand">
    select
        id,brand_name as brandName,
    	company_name as companyName,
    	ordered,
    	description,status
    from tb_brand
</select>

2.sql映射文件中,起别名:抽取字段 sql

<sql id="brand_column">
    id,brand_name as brandName,company_name as companyName,ordered,description,status
</sql>
<select id="findAllBrand" resultType="org.example.pojo.Brand">
    select
    	<!--引用sql片段-->
    	<include refid="brand_column"></include>
    from tb_brand
</select>

3.sql映射文件中,定义resultMap标签:

<!--1.定义resultMap标签-->
<resultMap id="brandResultMap" type="org.example.pojo.Brand">
    <result column="brand_name" property="brandName"></result>
    <result column="company_name" property="companyName"></result>
</resultMap>
<!--2.在<select>查询标签中使用resultMap属性替换resultType属性-->
<select id="findAllBrand" resultMap="brandResultMap">
    select * from tb_brand
</select>

4.在核心配置文件mybatis-config.xml中开启驼峰命名转换

<settings>
<!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <!--mapUnderscoreToCamelCase , true 表示开启-->
</settings>

4.1.2查看详情

查看某一条数据的详细信息

  • 1.编写Mapper接口方法:findByID();
    • 参数:id
    • 结果(返回值):Brand
  • 2.编写SQL语句:SQL映射文件
  • 3.执行方法,测试
<select id="findByID" resultType="Brand" parameterType="int">
    SELECT * FROM tb_brand where id = #{id}
<!--
    * 参数占位符:
        1.#{}:会将其替换为?,会自动添加单引号,防止sql注入
        2.${}:会直接将其替换为字符串,直接拼接,不会添加单引号,存在sql注入
        3.使用场景:
            * 参数传递的时候:#{}
            * 表名或者列名不固定的时候,模糊查询:${} ,存在sql注入
    * parameterType:参数类型,可以省略
    * 特殊字符处理:
        1.转义字符
           < 小于号: &lt;
        2.使用CDATA区
          小于号: <![CDATA[ < ]]>
-->
</select>

4.1.3条件查询

  • 多条件查询

1.编写接口方法:selectByCondition();
参数:所有查询条件
结果:List<Brand>

// 多条件查询,方法重载
// 1.散装参数:如果方法中有多个参数,需要使用@Param(“sql语句中使用的参数名”)注解指定参数名
List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName, @Param("brandName") String brandName);

//2.对象参数,实体类参数:只需要保证sql中的参数名和实体类属性名对应上,即可设置成功
List<Brand> selectByCondition(Brand brand);

//3.使用map集合参数,只需要保证sql中的参数名和map集合的键的名称对应上,即可设置成功
List<Brand> selectByCondition(Map map);

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

<select id="selectByCondition" resultType="Brand">
    select * from tb_brand where STATUS = #{status}
    AND company_name LIKE #{companyName}
    AND brand_name LIKE #{brandName};
</select>

3.执行方法,测试

@Test
public void testSelectByCondition() {
    int status = 1;
    String companyName = "华为";
    String brandName = "华为";
    companyName = "%" + companyName + "%";
    brandName = "%" + brandName + "%";
    //1.散装参数
    // List<Brand> brands = mapper.selectByCondition(status, companyName, brandName);

	//2.实体类参数:
    // Brand brand = new Brand();
    // brand.setStatus(status);
    // brand.setCompanyName(companyName);
    // brand.setBrandName(brandName);
    // List<Brand> brands = mapper.selectByCondition(brand);
	
	//3.map集合
    Map map = new HashMap();
    map.put("status", status);
    map.put("companyName", companyName);
    map.put("brandName", brandName);
    List<Brand> brands = mapper.selectByCondition(map);
    
    brands.forEach(System.out::println);
}

存在bug:当条件不满足3个(即两个及以下)时,会出现错误,那么就需要用到动态SQL了。

  • 多条件—动态条件查询,SQL语句会随着用户的输入或外部条件的变化而变化,成为动态SQL
<!--    动态条件查询
        使用if标签:条件判断 用于判断参数是否有值,使用test属性进行条件判断
            test:逻辑表达式
            问题:第一个条件不需要逻辑运算符and
            解决:恒式过度一下 where 1=1
            <where> 替换 where关键字
-->
<select id="selectByCondition" resultType="Brand">
    select * from tb_brand
    <where>
        <if test="status != null">
        	and 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="org.example.pojo.Brand">
    select * from tb_brand
    where     <!--可换成<where>标签,-->
    <choose> <!--相当于switch-->
        <when test="status != null"> <!--相当于case-->
            status = #{status}
        </when>
        <when test="companyName != null && companyName != ''"> <!--相当于case-->
            company_name LIKE '%${companyName}%'
        </when>
        <when test="brandName != null && brandName !=''"> <!--相当于case-->
            brand_name LIKE '%${brandName}%'
        </when>
        <otherwise> <!--相当于default,当使用<where>标签时,可省略<otherwise>-->
            1=1
        </otherwise>
    </choose>
</select>

4.2、添加

4.2.1添加

1.编写接口方法:void add(Brand brand);

  • 参数:除了id之外的所有数据
  • 返回值:void

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

<!--添加数据-->
<insert id="addBrand">
    insert into tb_brand values(null,#{brandName},#{companyName},#{ordered},#{description},#{status});
</insert>

3.执行方法,测试

@Test
public void testAddBrand() {
    Brand brand = new Brand(0, "小米", "小米", 1, "www.xiaomi.com", 100);
    mapper.addBrand(brand);
    // mybatis默认开启事务,需要手动提交事务
    // 如果没有手动提交,mybatis默认回滚事务。
    // 在获取sqlSession对象时,设置openSession(true),即自动提交事务
    sqlSession.commit();
}
  • MyBatis事务:
  • openSession():默认开启事务,进行增删改操作后需要使用sqlSession.commit();手动提交事务
  • openSession(true):可以设置为自动提交事务(关闭事务)

4.2.2添加 - 主键返回

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

比如:添加订单和订单项

1.添加订单

2.添加订单项,订单项中需要设置所属订单的id

<!--添加数据-->
<insert id="addBrand" useGeneratedKeys="true" keyProperty="id">
    <!--添加主键返回 useGeneratedKeys="true" keyProperty="id"-->
    insert into tb_brand values(null,
    #{brandName},
    #{companyName},
    #{ordered},
    #{description},
    #{status});
</insert>

4.3、修改

4.3.1修改全部字段

1.编写接口方法:void update(Brand brand);

  • 参数:所有数据
  • 返回值:void

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

<!--修改-->
<update id="updateBrand">
    update tb_brand
    set
    brand_name=#{brandName},
    company_name=#{companyName},
    ordered=#{ordered},
    description=#{description},
    status=#{status}
    where
    id=#{id};
</update>

3.执行方法,测试

/**
 * 修改数据
 */
@Test
public void testUpdateBrand() {
    int i = mapper.updateBrand(new Brand(2, "华为", "华为", 1, "www.huawei.com", 100));
    // 返回受影响的行数
    System.out.println(i);
}

4.3.2修改动态字段

1.编写接口方法:

  • 参数:部分数据,封装到对象中
  • 返回值:void

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

<update id="updateBrand">
    update tb_brand
    <set>
        <if test="brandName != null and brandName != '' ">
        	brand_name=#{brandName},			
        </if>
        <if test="companyName != null and companyName != '' ">
        	company_name=#{companyName},
        </if>
        <if test="ordered != null ">
        	ordered=#{ordered},
        </if>
        <if test="description != null and description != '' ">
        	description=#{description},
        </if>
        <if test="status != null  ">
        	status=#{status}	
        </if>
    </set>
    where id=#{id};
</update>

3.执行方法,测试

@Test
public void testUpdateBrand() {
    int id = 2;
    String companyName = "华为技术有限公司";
    Brand brand =new Brand();
    brand.setId(id);
    brand.setCompanyName(companyName);
    int i = mapper.updateBrand(brand);
    System.out.println(i);
}

4.4、删除

4.4.1删除一个

1.编写接口方法:void deleteById(int id);

  • 参数:id
  • 返回值:void

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

<!--删除一个-->
<delete id="deleteById">
    delete from tb_brand where id=#{id};
</delete>

3.执行方法,测试

/**
 * 删除单个数据
 */
@Test
public void testDeleteBrand() {
     mapper.deleteById(6);
}

4.4.2批量删除

1.编写接口方法:deleteByIds(@Param(“ids”)int[] ids);

  • 参数:ids数组
  • 返回值:void

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

<!--批量删除-->
<delete id="deleteByIds">
    delete from tb_brand where id in
    <!--
    mybatis会将数组参数,封装为一个Map集合,
    默认数组名称:array
    使用@Param注解改变map集合的默认key的名称,即array
    没加@Param注解,就得使用array

            collection:遍历数组
            separator:分隔符
            open:开始符号
            close:结束符号-->
    <foreach collection="ids" item="id" separator="," open="(" close=")">
        #{id}
    </foreach>
</delete>

3.执行方法,测试

//删除多个数据
@Test
public void testDeleteByIds() {
    int[] ids = {13, 12, 11};
    mapper.deleteByIds(ids);
}

五、mybatis参数传递

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

单个参数:

  • 1.POJO类型:直接使用,属性名和参数占位符名称一致
  • 2.Map集合:直接使用,键名和参数占位符名称一致
  • 3.Collection:封装为map集合,可以使用@Param注解:替换Map集合中默认的arg键名
    • map.put(“arg0”,collection集合)
    • map.put(“collection”,collection集合)
  • 4.List:封装为Map集合,可以使用@Param注解:替换Map集合中默认的arg键名
    • map.put(“arg0”,list集合)
    • map.put(“collection”,list集合)
    • map.put(“list”,list集合)
  • 5.Array:封装为map集合,可以使用@Param注解:替换Map集合中默认的arg键名
    • map.put(“arg0”,数组)
    • map.put(“array”,数组)
  • 6.其他类型:直接使用

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

map.put(“arg0”,参数值1)

map.put(“param1”,参数值1)

map.put(“param2”,参数值2)

map.put(“arg1”,参数值2)

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

六、注解开发

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

public interface BlogMapper {
  @Select("SELECT * FROM blog WHERE id = #{id}")
  Blog selectBlog(int id);
}

查询:@Select

添加:@Insert

修改:@Update

删除:@Delete

提示:

1.注解完成简单功能

2.配置文件完成复杂功能

使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。

选择何种方式来配置映射,以及是否应该要统一映射语句定义的形式,完全取决于你和你的团队。 换句话说,永远不要拘泥于一种方式,你可以很轻松地在基于注解和 XML 的语句映射方式间自由移植和切换。


  1. 持久层:负责将数据保存到数据库的那一层代码。Java EE三层架构:表现层、业务层、持久层 ↩︎

  2. 框架:框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型。在框架的基础之上构建软件编写更加高效、规范、通用、可扩展 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值