mybatis(二)

news2024/9/23 13:29:24

mybatis练习---2种方式

  • 能够使用映射配置文件实现CRUD操作
  • 能够使用注解实现CRUD操作

配置文件CRUD就是把sql语句写到配置文件中,注解CRUD就是吧sql语句写到注解上。

一、配置文件实现CRUD

如上图所示产品原型,里面包含了品牌数据的 查询 按条件查询 添加 删除 批量删除 修改 等功能,而这些功能其实 就是对数据库表中的数据进行CRUD 操作。接下来我们就使用 Mybatis 完成品牌数据的增删改查操作。以下是我们要完成功能 列表:

1.1 环境准备

  •  数据库表(tb_brand)及数据准备
create database mybatis;
use mybatis;

drop table if exists tb_user;

create table tb_user(
	id int primary key auto_increment,
	username varchar(20),
	password varchar(20),
	gender char(1),
	addr varchar(30)
);



INSERT INTO tb_user VALUES (1, 'zhangsan', '123', '男', '北京');
INSERT INTO tb_user VALUES (2, '李四', '234', '女', '天津');
INSERT INTO tb_user VALUES (3, '王五', '11', '男', '西安');

  • 实体类 Brand
com.itheima.pojo 包下创建 Brand 实体类。
public class Brand {
    // id 主键
    private Integer id;
    // 品牌名称
    private String brandName;
    // 企业名称
    private String companyName;
    // 排序字段
    private Integer ordered;
    // 描述信息
    private String description;
    // 状态:0:禁用  1:启用
    private Integer status;

//省略 setter and getter。自己写时要补全这部分代码

}
  • 编写测试用例
测试代码需要在 test/java 目录下创建包及测试用例。项目结构如下:

  • 安装 MyBatisX 插件
MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。
主要功能:
1.XML 映射配置文件 和 接口方法 间相互跳转
2.根据接口方法生成 statement
安装方式:
点击 file ,选择 settings ,就能看到如下图所示界面

注意:安装完毕后需要重启 IDEA
插件效果。--看到小鸟出来了,蓝色的小鸟是mapper接口,红色的小鸟是sql映射文件。在statement这个位置,我想要找到selectAll()方法,直接点击那个鸟(蓝色的小鸟)就可以直接跳转过来,如果想要看这个方法对应的sql语句,还是直接点击那个鸟(红色的小鸟)就可以跳转过来

红色头绳的表示映射配置文件,蓝色头绳的表示 mapper 接口。在 mapper 接口点击红色头绳的小鸟图标会自动跳转 到对应的映射配置文件,在映射配置文件中点击蓝色头绳的小鸟图标会自动跳转到对应的mapper 接口。也可以在 mapper接口中定义方法,自动生成映射配置文件中的 statement ,如图所示

比如你在mapper接口写上selectById()方法,看到报错,说这个sql语句selectById没有定义在配置文件中。它可以帮助我们定义按住enter+alt快捷键,然后就可以在配置文件写sql语句了。

1.2 查询所有数据

第一就是我们就写好mapper接口方法,第二就是通过mybatisX插件生成对应的sql statement,它只能生成对应的sql标签,以及id和resultType,其他的sql语句要自己写,第三就是写对应的测试方法。 

1.编写接口方法

BrandMapper 
public interface BrandMapper {


    /**
     * 查询所有
     */
    List<Brand> selectAll();


}

2.编写SQL语句

BrandMapper.xml

<!--
    namespace:名称空间
-->

<mapper namespace="com.itheima.mapper.BrandMapper">

  <select id="selectAll" resultMap="brandResultMap">
        select *
        from tb_brand;
    </select>

</mapper>

3.编写测试方法
MyBatisTest

public class MyBatisTest {


    @Test
    public void testSelectAll() throws IOException {
        //1. 获取SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

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

        //3. 获取Mapper接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        //4. 执行方法
        List<Brand> brands = brandMapper.selectAll();
        System.out.println(brands);

        //5. 释放资源
        sqlSession.close();

    }

}

4.起别名解决上述问题--用as关键字

BrandMapper.xml

注意:

1.mybatis数据库的列名,跟封装对象的各个属性名必须是完全相同的,不相同会出现取值为空。

2.因为jdbc是通过赋值吧数据库数据赋值到对象属性的,所以属性名可以不同,但是这里对象属性必须和数据库当中的名字一样,要不然为null。

3.定义sql片段(id去定义),然后再去引用它(通过include中的refid属性),有点类似css中的id选择器。

4.resultMap标签有2个属性,id是唯一标识,可以随便起个别名。type是当前的resultMap要和那个类型映射。id是主键映射,result是一般字段映射。

从上面结果可以看到 brandName companyName 这两个属性的数据没有封装成功,查询 实体类 和 表中的字段 发现,在 实体类中属性名是 brandName companyName ,而表中的字段名为 brand_name company_name ,如下图所示 。那么 我们只需要保持这两部分的名称一致这个问题就迎刃而解。

我们可以在写 sql 语句时给这两个字段起别名,将别名定义成和属性名一致即可。
<!--
    namespace:名称空间
-->

<mapper namespace="com.itheima.mapper.BrandMapper">

    <!--
        数据库表的字段名称  和  实体类的属性名称 不一样,则不能自动封装数据
          方法一:起别名:对不一样的列名起别名,让别名和实体类的属性名一样
                * 缺点:每次查询都要定义一次别名
          方法二: sql片段
                * 缺点:不灵活
          方法三: resultMap:
                1. 定义<resultMap>标签
                2. 在<select>标签中,使用resultMap属性替换 resultType属性

    -->
 


    <!--
        方法一:起别名--用as起别名
    -->
 

    <!--<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去定义),然后再去引用它(通过include标签中的refid属性),有点类似css中的id选择器
    -->

  <!--   <sql id="brand_column">
         id, brand_name as brandName, company_name as companyName, ordered, description, status
     </sql>

 -->

    <!--

     <select id="selectAll" resultType="brand">
         select
             <include refid="brand_column" />
         from tb_brand;
     </select>


     -->
    <!--<select id="selectAll" resultType="brand">
        select *
        from tb_brand;
    </select>-->

</mapper>

5.使用resultMap解决上述问题---这种用的多

BrandMapper.xml

<!--
    namespace:名称空间
-->

<mapper namespace="com.itheima.mapper.BrandMapper">

    <!--
        数据库表的字段名称  和  实体类的属性名称 不一样,则不能自动封装数据
          方法一:起别名:对不一样的列名起别名,让别名和实体类的属性名一样
                * 缺点:每次查询都要定义一次别名
          方法二: sql片段
                * 缺点:不灵活
          方法三: resultMap:
                1. 定义<resultMap>标签
                2. 在<select>标签中,使用resultMap属性替换 resultType属性

    -->
    <!--
方法三: resultMap标签
        id:唯一标识
        type:映射的类型,支持别名
    -->
    <resultMap id="brandResultMap" type="brand">
        <!--
            id:完成主键字段的映射
                column:表的列名
                property:实体类的属性名
            result:完成一般字段的映射
                column:表的列名
                property:实体类的属性名
        -->
        <result column="brand_name" property="brandName"/>
        <result column="company_name" property="companyName"/>
    </resultMap>



    <select id="selectAll" resultMap="brandResultMap">
        select *
        from tb_brand;
    </select>


    <!--
        方法一:起别名--用as起别名
    -->
 

    <!--<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去定义),然后再去引用它(通过include标签中的refid属性),有点类似css中的id选择器
    -->

  <!--   <sql id="brand_column">
         id, brand_name as brandName, company_name as companyName, ordered, description, status
     </sql>

 -->

    <!--

     <select id="selectAll" resultType="brand">
         select
             <include refid="brand_column" />
         from tb_brand;
     </select>


     -->
    <!--<select id="selectAll" resultType="brand">
        select *
        from tb_brand;
    </select>-->

</mapper>

6.小结

1.3 查看详情

在每个数据后面都有一个查看详情按钮,你点击它他会把商品详细信息列表展示在这里。每一条记录都可以查看详情,查看详情就是查看某条数据的详细信息。你单击查看详情按钮他会把当前数据的id传到后台java代码,java代码根据id完成查询,查完之后会把这条记录的数据对象返回给页面,页面就会展示这条记录详细信息。

1.编写接口方法

BrandMapper 接口中定义根据id查询数据的方法

/**
* 查看详情:根据 Id 查询
*/
Brand selectById ( int id );

2.编写SQL语句

BrandMapper.xml 映射配置文件中编写 statement ,使用 resultMap 而不是使用 resultType
<select id = "selectById" resultMap = "brandResultMap" >
select *
from tb_brand where id = #{id};
</select>
注意:上述 SQL 中的 #{id} 先这样写,一会我们再详细讲解

3.编写测试方法

test/java 下的 com.itheima.mapper 包下的 MybatisTest类中 定义测试方法

@Test
public void testSelectById () throws IOException {
// 接收参数,该 id 以后需要传递过来
int id = 1 ;
//1. 获取 SqlSessionFactory
String resource = "mybatis-config.xml" ;
InputStream inputStream = Resources . getResourceAsStream ( resource );
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder (). build ( inputStream );
//2. 获取 SqlSession 对象
SqlSession sqlSession = sqlSessionFactory . openSession ();
//3. 获取 Mapper 接口的代理对象
BrandMapper brandMapper = sqlSession . getMapper ( BrandMapper . class );
//4. 执行方法
Brand brand = brandMapper . selectById ( id );
System . out . println ( brand );
//5. 释放资源
sqlSession . close ();
}
执行测试方法结果如下:

4.参数占位符

查询到的结果很好理解就是 id 1 的这行数据。而这里我们需要看控制台显示的 SQL 语句,能看到使用?进行占位。说明我们 在映射配置文件中的写的 #{id} 最终会被?进行占位。接下来我们就聊聊映射配置文件中的参数占位符。
mybatis 提供了两种参数占位符:
  • #{} :执行SQL时,会将 #{} 占位符替换为?,将来自动设置参数值。从上述例子可以看出使用#{} 底层使用的是 PreparedStatement
  • ${} :拼接SQL。底层使用的是 Statement ,会存在SQL注入问题。如下图将 映射配置文件中的 #{} 替换成 ${} 来看效
<select id = "selectById" resultMap = "brandResultMap" >
select *
from tb_brand where id = ${id};
</select>
重新运行查看结果如下:

 注意:从上面两个例子可以看出,以后开发我们使用 #{} 参数占位符。

5.parameterType使用

对于有参数的 mapper 接口方法,我们在映射配置文件中应该配置 ParameterType 来指定参数类型。只不过该属性都可以 省略。如下图:
<select id = "selectById" parameterType = "int" resultMap = "brandResultMap" >
select *
from tb_brand where id = ${id};
</select>

6.SQL语句中特殊字段处理

以后肯定会在 SQL 语句中写一下特殊字符,比如某一个字段大于某个值,如下图

可以看出报错了,因为映射配置文件是 xml 类型的问题,而 > < 等这些字符在 xml 中有特殊含义,所以此时我们需要将这些符 号进行转义,可以使用以下两种方式进行转义
  • 转义字符
下图的 &lt ; 就是 < 的转义字符。

1.4 多条件查询

我们经常会遇到如上图所示的多条件查询,将多条件查询的结果展示在下方的数据列表中。而我们做这个功能需要分析最终
SQL 语句应该是什么样,思考两个问题
  • 条件表达式
  • 如何连接
条件字段 企业名称 品牌名称 需要进行模糊查询,所以条件应该是:

简单的分析后,我们来看功能实现的步骤:
  • 编写接口方法
参数:所有查询条件
结果: List
  • 在映射配置文件中编写SQL语句
  • 编写测试方法并执行

1.编写接口方法

BrandMapper 接口中定义多条件查询的方法。
而该功能有三个参数,我们就需要考虑定义接口时,参数应该如何定义。 Mybatis 针对多参数有多种实现
  • 使用 @Param("参数名称") 标记每一个参数,在映射配置文件中就需要使用 #{参数名称} 进行占位
List < Brand > selectByCondition ( @Param ( "status" ) int status , @Param ( "companyName" ) String
companyName , @Param ( "brandName" ) String brandName );
  • 将多个参数封装成一个 实体对象 ,将该实体对象作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{} 时,里面的内容必须和实体类属性名保持一致。
List < Brand > selectByCondition ( Brand brand );
  • 将多个参数封装到map集合中,将map集合作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和map集合中键的名称一致。
List<Brand> selectByCondition(Map map);

2.编写SQL语句

BrandMapper.xml 映射配置文件中编写 statement ,使用 resultMap 而不是使用 resultType

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

3.编写测试方法

test/java 下的 com.itheima.mapper 包下的 MybatisTest类中 定义测试方法

@Test
public void testSelectByCondition () throws IOException {
// 接收参数
int status = 1 ;
String companyName = " 华为 " ;
String brandName = " 华为 " ;
// 处理参数
companyName = "%" + companyName + "%" ;
brandName = "%" + brandName + "%" ;
//1. 获取 SqlSessionFactory
String resource = "mybatis-config.xml" ;
InputStream inputStream = Resources . getResourceAsStream ( resource );
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder (). build ( inputStream );
//2. 获取 SqlSession 对象
SqlSession sqlSession = sqlSessionFactory . openSession ();
//3. 获取 Mapper 接口的代理对象
BrandMapper brandMapper = sqlSession . getMapper ( BrandMapper . class );
//4. 执行方法
// 方式一 :接口方法参数使用 @Param 方式调用的方法
//List<Brand> brands = brandMapper.selectByCondition(status, companyName, brandName);
// 方式二 :接口方法参数是 实体类对象 方式调用的方法
// 封装对象
/* Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);*/
//List<Brand> brands = brandMapper.selectByCondition(brand);
// 方式三 :接口方法参数是 map 集合对象 方式调用的方法
Map map = new HashMap ();
map . put ( "status" , status );
map . put ( "companyName" , companyName );
map . put ( "brandName" , brandName );
List < Brand > brands = brandMapper . selectByCondition ( map );
System . out . println ( brands );
//5. 释放资源
sqlSession . close ();
}

4.动态SQL

上述功能实现存在很大的问题。用户在输入条件时,肯定不会所有的条件都填写,这个时候我们的 SQL 语句就不能那样写的
例如用户只输入 当前状态 时, SQL 语句就是
select * from tb_brand where status = #{status}
而用户如果只输入企业名称时, SQL 语句就是
select * from tb_brand where company_name like #{companName}
而用户如果输入了 当前状态 企业名称 时, SQL 语句又不一样
select * from tb_brand where status = #{status} and company_name like #{companName}
针对上述的需要, Mybatis 对动态 SQL 有很强大的支撑:
  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach
我们先学习 if 标签和 where 标签:
  • if 标签:条件判断
test 属性:逻辑表达式
<select id = "selectByCondition" resultMap = "brandResultMap" >
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>
</select>
如上的这种 SQL 语句就会根据传递的参数值进行动态的拼接。如果此时 status companyName 有值那么就会值拼接这 两个条件。  
执行结果如下:
但是它也存在问题,如果此时给的参数值是
Map map = new HashMap ();
// map.put("status" , status);
map . put ( "companyName" , companyName );
map . put ( "brandName" , brandName );
拼接的 SQL 语句就变成了
select * from tb_brand where and company_name like ? and brand_name like ?
而上面的语句中 where 关键后直接跟 and 关键字,这就是一条错误的 SQL 语句。这个就可以使用 where 标签解决
  • where 标签
作用:
替换 where 关键字
会动态的去掉第一个条件前的 and
如果所有的参数没有值则不加 where 关键字
<select id = "selectByCondition" resultMap = "brandResultMap" >
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>
注意:需要给每个条件前都加上 and 关键字。

1.5 单个条件(动态SQL

如上图所示,在查询时只能选择 品牌名称 当前状态 企业名称 这三个条件中的一个,但是用户到底选择哪儿一个,我们并 不能确定。这种就属于单个条件的动态SQL 语句。
这种需求需要使用到 choose (when, otherwise )标签 实现, 而 choose 标签类似于 Java 中的 switch 语句。
通过一个案例来使用这些标签

1.编写接口方法

BrandMapper 接口中定义单条件查询的方法。

/**
* 单条件动态查询
* @param brand
* @return
*/
List < Brand > selectByConditionSingle ( Brand brand );

2.编写SQL语句

BrandMapper.xml 映射配置文件中编写 statement ,使用 resultMap 而不是使用 resultType

<select id = "selectByConditionSingle" resultMap = "brandResultMap" >
select *
from tb_brand
<where>
<choose> <!-- 相当于 switch-->
<when test = "status != null" > <!-- 相当于 case-->
status = #{status}
</when>
<when test = "companyName != null and companyName != '' " > <!-- 相当于 case-->
company_name like #{companyName}
</when>
<when test = "brandName != null and brandName != ''" > <!-- 相当于 case-->
brand_name like #{brandName}
</when>
</choose>
</where>
</select>

3.编写测试方法

test/java 下的 com.itheima.mapper 包下的 MybatisTest类中 定义测试方法

@Test
public void testSelectByConditionSingle () throws IOException {
// 接收参数
int status = 1 ;
String companyName = " 华为 " ;
String brandName = " 华为 " ;
// 处理参数
companyName = "%" + companyName + "%" ;
brandName = "%" + brandName + "%" ;
// 封装对象
Brand brand = new Brand ();
//brand.setStatus(status);
brand . setCompanyName ( companyName );
//brand.setBrandName(brandName);
//1. 获取 SqlSessionFactory
String resource = "mybatis-config.xml" ;
InputStream inputStream = Resources . getResourceAsStream ( resource );
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder (). build ( inputStream );
//2. 获取 SqlSession 对象
SqlSession sqlSession = sqlSessionFactory . openSession ();
//3. 获取 Mapper 接口的代理对象
BrandMapper brandMapper = sqlSession . getMapper ( BrandMapper . class );
//4. 执行方法
List < Brand > brands = brandMapper . selectByConditionSingle ( brand );
System . out . println ( brands );
//5. 释放资源
sqlSession . close ();
}

执行测试方法结果如下:

1.6 添加数据

如上图是我们平时在添加数据时展示的页面,而我们在该页面输入想要的数据后添加 提交 按钮,就会将这些数据添加到数 据库中。接下来我们就来实现添加数据的操作。
  • 编写接口方法

参数:除了 id 之外的所有的数据。 id 对应的是表中主键值,而主键我们是 自动增长 生成的。
  • 编写SQL语句

  • 编写测试方法并执行
明确了该功能实现的步骤后,接下来我们进行具体的操作。

1.编写接口方法

BrandMapper 接口中定义添加方法。

/**
* 添加
*/
void add ( Brand brand );

2.编写SQL语句

BrandMapper.xml 映射配置文件中编写添加数据的 statement

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

3.编写测试方法

test/java 下的 com.itheima.mapper 包下的 MybatisTest类中 定义测试方法

@Test
public void testAdd () throws IOException {
// 接收参数
int status = 1 ;
String companyName = " 波导手机 " ;
String brandName = " 波导 " ;
String description = " 手机中的战斗机 " ;
int ordered = 100 ;
// 封装对象
Brand brand = new Brand ();
brand . setStatus ( status );
brand . setCompanyName ( companyName );
brand . setBrandName ( brandName );
brand . setDescription ( description );
brand . setOrdered ( ordered );
//1. 获取 SqlSessionFactory
String resource = "mybatis-config.xml" ;
InputStream inputStream = Resources . getResourceAsStream ( resource );
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder (). build ( inputStream );
//2. 获取 SqlSession 对象
SqlSession sqlSession = sqlSessionFactory . openSession ();
//SqlSession sqlSession = sqlSessionFactory.openSession(true); // 设置自动提交事务,这种情况不需
要手动提交事务了
//3. 获取 Mapper 接口的代理对象
BrandMapper brandMapper = sqlSession . getMapper ( BrandMapper . class );
//4. 执行方法
brandMapper . add ( brand );
// 提交事务
sqlSession . commit ();
//5. 释放资源
sqlSession . close ();
}
执行结果如下:

4.添加-主键返回

在数据添加成功后,有时候需要获取插入数据库数据的主键(主键是自增长)。
比如:添加订单和订单项,如下图就是京东上的订单

订单数据存储在订单表中,订单项存储在订单项表中。
  • 添加订单数据

  • 添加订单项数据,订单项中需要设置所属订单的id

明白了什么时候 主键返回 。接下来我们简单模拟一下,在添加完数据后打印 id 属性值,能打印出来说明已经获取到了。
我们将上面添加品牌数据的案例中映射配置文件里 statement 进行修改,如下
<insert id = "add" useGeneratedKeys = "true" keyProperty = "id" >
insert into tb_brand (brand_name, company_name, ordered, description, status)
values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});
</insert>
insert 标签上添加如下属性:
  • useGeneratedKeys:是够获取自动增长的主键值。true表示获取
  • keyProperty :指定将获取到的主键值封装到哪儿个属性里

1.7 修改

如图所示是修改页面,用户在该页面书写需要修改的数据,点击 提交 按钮,就会将数据库中对应的数据进行修改。注意一 点,如果哪儿个输入框没有输入内容,我们是将表中数据对应字段值替换为空白还是保留字段之前的值?答案肯定是保留之 前的数据。
接下来我们就具体来实现

1.编写接口方法

BrandMapper 接口中定义修改方法。

/**
* 修改
*/
void update ( Brand brand );

上述方法参数 Brand 就是封装了需要修改的数据,而id肯定是有数据的,这也是和添加方法的区别。

2.编写SQL语句

BrandMapper.xml 映射配置文件中编写修改数据的 statement

<update id = "update" >
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>
set 标签可以用于动态包含需要更新的列,忽略其它不更新的列。

3.编写测试方法

test/java 下的 com.itheima.mapper 包下的 MybatisTest类中 定义测试方法

@Test
public void testUpdate () throws IOException {
// 接收参数
int status = 0 ;
String companyName = " 波导手机 " ;
String brandName = " 波导 " ;
String description = " 波导手机 , 手机中的战斗机 " ;
int ordered = 200 ;
int id = 6 ;
// 封装对象
Brand brand = new Brand ();
brand . setStatus ( status );
// brand.setCompanyName(companyName);
// brand.setBrandName(brandName);
// brand.setDescription(description);
// brand.setOrdered(ordered);
brand . setId ( id );
//1. 获取 SqlSessionFactory
String resource = "mybatis-config.xml" ;
InputStream inputStream = Resources . getResourceAsStream ( resource );
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder (). build ( inputStream );
//2. 获取 SqlSession 对象
SqlSession sqlSession = sqlSessionFactory . openSession ();
//SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3. 获取 Mapper 接口的代理对象
BrandMapper brandMapper = sqlSession . getMapper ( BrandMapper . class );
//4. 执行方法
int count = brandMapper . update ( brand );
System . out . println ( count );
// 提交事务
sqlSession . commit ();
//5. 释放资源
sqlSession . close ();
}
执行测试方法结果如下:

从结果中 SQL 语句可以看出,只修改了 status 字段值,因为我们给的数据中只给 Brand 实体对象的 status 属性设置值 了。这就是 set 标签的作用。

1.8 删除一行数据

如上图所示,每行数据后面都有一个 删除 按钮,当用户点击了该按钮,就会将改行数据删除掉。那我们就需要思考,这种 删除是根据什么进行删除呢?是通过主键id 删除,因为 id 是表中数据的唯一标识。
接下来就来实现该功能。

1.编写接口方法

BrandMapper 接口中定义根据id删除方法。

/**
* 根据 id 删除
*/
void deleteById ( int id );

2.编写SQL语句

BrandMapper.xml 映射配置文件中编写删除一行数据的 statement

<delete id = "deleteById" >
delete from tb_brand where id = #{id};
</delete>

3.编写测试方法

test/java 下的 com.itheima.mapper 包下的 MybatisTest类中 定义测试方法

@Test
public void testDeleteById () throws IOException {
// 接收参数
int id = 6 ;
//1. 获取 SqlSessionFactory
String resource = "mybatis-config.xml" ;
InputStream inputStream = Resources . getResourceAsStream ( resource );
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder (). build ( inputStream );
//2. 获取 SqlSession 对象
SqlSession sqlSession = sqlSessionFactory . openSession ();
//SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3. 获取 Mapper 接口的代理对象
BrandMapper brandMapper = sqlSession . getMapper ( BrandMapper . class );
//4. 执行方法
brandMapper . deleteById ( id );
// 提交事务
sqlSession . commit ();
//5. 释放资源
sqlSession . close ();
}
运行过程只要没报错,直接到数据库查询数据是否还存在。

1.9 批量删除

如上图所示,用户可以选择多条数据,然后点击上面的 删除 按钮,就会删除数据库中对应的多行数据。

1.编写接口方法

BrandMapper 接口中定义删除多行数据的方法。
/**
* 批量删除
*/
void deleteByIds ( int [] ids );
参数是一个数组,数组中存储的是多条数据的 id

2.编写SQL语句

BrandMapper.xml 映射配置文件中编写删除多条数据的 statement
编写 SQL 时需要遍历数组来拼接 SQL 语句。 Mybatis 提供了 foreach 标签供我们使用
foreach 标签
用来迭代任何可迭代的对象(如数组,集合)。
  • collection 属性:
mybatis 会将数组参数,封装为一个 Map 集合。
默认: array = 数组
使用 @Param 注解改变 map 集合的默认 key 的名称
  • item 属性:本次迭代获取到的元素。
  • separator 属性:集合项迭代之间的分隔符。 foreach 标签不会错误地添加多余的分隔符。也就是最后一次迭代不会加 分隔符。
  • open 属性:该属性值是在拼接SQL语句之前拼接的语句,只会拼接一次
  • close 属性:该属性值是在拼接SQL语句拼接后拼接的语句,只会拼接一次

<delete id = "deleteByIds" >
delete from tb_brand where id
in
<foreach collection = "array" item = "id" separator = "," open = "(" close = ")" >
#{id}
</foreach>
;
</delete>
假如数组中的 id 数据是 {1,2,3} ,那么拼接后的 sql 语句就是:
delete from tb_brand where id in ( 1 , 2 , 3 ) ;

3.编写测试方法

test/java 下的 com.itheima.mapper 包下的 MybatisTest类中 定义测试方法

@Test
public void testDeleteByIds () throws IOException {
// 接收参数
int [] ids = { 5 , 7 , 8 };
//1. 获取 SqlSessionFactory
String resource = "mybatis-config.xml" ;
InputStream inputStream = Resources . getResourceAsStream ( resource );
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder (). build ( inputStream );
//2. 获取 SqlSession 对象
SqlSession sqlSession = sqlSessionFactory . openSession ();
//SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3. 获取 Mapper 接口的代理对象
BrandMapper brandMapper = sqlSession . getMapper ( BrandMapper . class );
//4. 执行方法
brandMapper . deleteByIds ( ids );
// 提交事务
sqlSession . commit ();
//5. 释放资源
sqlSession . close ();
}

1.10 Mybatis参数传递

Mybatis 接口方法中可以接收各种各样的参数,如下:
  • 多个参数
  • 单个参数:单个参数又可以是如下类型
POJO 类型
Map 集合类型
Collection 集合类型
List 集合类型
Array 类型
其他类型

1.多个参数

如下面的代码,就是接收两个参数,而接收多个参数需要使用 @Param 注解,那么为什么要加该注解呢?这个问题要弄明白 就必须来研究Mybatis 底层对于这些参数是如何处理的。
User select ( @Param ( "username" ) String username , @Param ( "password" ) String password );
<select id = "select" resultType = "user" >
select *
from tb_user
where
username=#{username}
and password=#{password}
</select>
我们在接口方法中定义多个参数, Mybatis 会将这些参数封装成 Map 集合对象,值就是参数值,而键在没有使用 @Param 注解时有以下命名规则:
  • arg 开头 :第一个参数就叫 arg0,第二个参数就叫 arg1,以此类推。如:
map.put("arg0" ,参数值 1);
map.put("arg1" ,参数值 2);
  • param 开头 : 第一个参数就叫 param1,第二个参数就叫 param2,依次类推。如:
map.put("param1" ,参数值 1);
map.put("param2" ,参数值 2);
代码验证:
UserMapper 接口中定义如下方法
User select ( String username , String password );
UserMapper.xml 映射配置文件中定义 SQL
<select id = "select" resultType = "user" >
select *
from tb_user
where
username=#{arg0}
and password=#{arg1}
</select>
或者
<select id = "select" resultType = "user" >
select *
from tb_user
where
username=#{param1}
and password=#{param2}
</select>
运行代码结果如下

在映射配合文件的 SQL 语句中使用用 arg 开头的和 param 书写,代码的可读性会变的特别差,此时可以使用 @Param 注解。
在接口方法参数上使用 @Param 注解, Mybatis 会将 arg 开头的键名替换为对应注解的属性值。
代码验证:
  • UserMapper 接口中定义如下方法,在 username 参数前加上 @Param 注解
User select ( @Param ( "username" ) String username , String password );
Mybatis 在封装 Map 集合时,键名就会变成如下:
map.put("username" ,参数值 1);
map.put("arg1" ,参数值 2);
map.put("param1" ,参数值 1);
map.put("param2" ,参数值 2);
  • UserMapper.xml 映射配置文件中定义SQL
<select id = "select" resultType = "user" >
select *
from tb_user
where
username=#{username}
and password=#{param2}
</select>
  • 运行程序结果没有报错。而如果将 #{} 中的 username 还是写成 arg0
<select id = "select" resultType = "user" >
select *
from tb_user
where
username=#{arg0}
and password=#{param2}
</select>
  • 运行程序则可以看到错误

结论:以后接口参数是多个时,在每个参数上都使用 @Param 注解。这样代码的可读性更高。

2.单个参数

  • POJO 类型
直接使用。要求 属性名 参数占位符名称 一致
  • Map 集合类型
直接使用。要求 map 集合的键名 参数占位符名称 一致
  • Collection 集合类型
Mybatis 会将集合封装到 map 集合中,如下:
map.put("arg0" collection 集合 );
map.put("collection" collection 集合 ;
可以使用 @Param 注解替换 map 集合中默认的 arg 键名。
  • List 集合类型
Mybatis 会将集合封装到 map 集合中,如下:
map.put("arg0" list 集合 );
map.put("collection" list 集合 );
map.put("list" list 集合 );
可以使用 @Param 注解替换 map 集合中默认的 arg 键名。
  • Array 类型
Mybatis 会将集合封装到 map 集合中,如下:
map.put("arg0" ,数组 );
map.put("array" ,数组 );
可以使用 @Param 注解替换 map 集合中默认的 arg 键名。
  • 其他类型
比如int 类型, 参数占位符名称 叫什么都可以。尽量做到见名知意

二、注解完成CRUD

使用注解开发会比配置文件开发更加方便。如下就是使用注解进行开发
@Select ( value = "select * from tb_user where id = #{id}" )
public User select ( int id );
注意:
  • 注解是用来替换映射配置文件方式配置的,所以使用了注解,就不需要再映射配置文件中书写对应的 statement
Mybatis 针对 CURD 操作都提供了对应的注解,已经做到见名知意。如下:
  • 查询 :@Select
  • 添加 :@Insert
  • 修改 :@Update
  • 删除 :@Delete
接下来我们做一个案例来使用 Mybatis 的注解开发
代码实现:
  • 将之前案例中 UserMapper.xml 中的 根据id查询数据 的 statement 注释掉

  • UserMapper 接口的 selectById 方法上添加注解

  • 运行测试程序也能正常查询到数据
我们课程上只演示这一个查询的注解开发,其他的同学们下来可以自己实现,都是比较简单。
注意: 在官方文档中 入门 中有这样的一段话:

所以, 注解完成简单功能,配置文件完成复杂功能。
而我们之前写的动态 SQL 就是复杂的功能,如果用注解使用的话,就需要使用到 Mybatis 提供的 SQL 构建器来完成,而对应 的代码如下:

上述代码将 java 代码和 SQL 语句融到了一块,使得代码的可读性大幅度降低。

三、动态SQL

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/400783.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用ControlNet 控制 Stable Diffusion

本文将要介绍整合HuggingFace的diffusers 包和ControlNet调节生成文本到图像&#xff0c;可以更好地控制文本到图像的生成 ControlNet是一种通过添加额外条件来控制扩散模型的神经网络结构。它提供了一种增强稳定扩散的方法&#xff0c;在文本到图像生成过程中使用条件输入&…

【工具使用】STM32CubeMX-基础使用篇

一、概述 无论是新手还是大佬&#xff0c;基于STM32单片机的开发&#xff0c;使用STM32CubeMX都是可以极大提升开发效率的&#xff0c;并且其界面化的开发&#xff0c;也大大降低了新手对STM32单片机的开发门槛。     本文主要面向初次接触STM32CubeMX的同学&#xff0c;大…

垃圾回收:垃圾数据如何自动回收

有些数据被使用之后&#xff0c;可能就不再需要了&#xff0c;我们把这种数据称为垃圾数据。如果这些垃圾数据一直保存在内存中&#xff0c;那么内存会越用越多&#xff0c;所以我们需要对这些垃圾数据进行回收&#xff0c;以释放有限的内存空间 不同语言的垃圾回收策略 通常…

「中华田园敏捷开发」,是老板无能还是程序员无力?

敏捷开发一直都是无数程序员的追求&#xff0c;也被被视为“开发者的福音”&#xff0c;但显然敏捷开发在中国落地的专业度还不够&#xff0c;以至于出现了“中华田园敏捷”的说法&#xff0c;什么叫“中华田园敏捷开发”&#xff1f; 简单点说&#xff1a;中华田园敏捷开发的…

异常(C++)

文章目录1. 概念1.1 C语言处理错误机制1.2 C异常机制throw表达式try...catch语句例子2. 抛出异常2.1 栈展开栈展开的例子2.2 栈展开过程中对象被自动销毁2.3 析构函数与异常内存泄漏2.4 异常对象3. 捕获异常3.1 捕获子类异常3.2 异常的重新抛出4. 异常安全4.2 例子不抛出异常保…

VIT(vision transformer)onnx模型解析

背景&#xff1a;transformer在CV领域的应用论文下载链接&#xff1a;https://arxiv.org/abs/2010.11929Pytorch实现代码&#xff1a; pytorch_classification/vision_transformer(太阳花的小绿豆博主实现的代码)有一些大神在研究关于CNNtransformer或者纯用transformer实现。原…

北邮22信通:你是不是在looking for……那串代码?(2)第三章单链表

相信有了第二章顺序表的基础&#xff0c;小伙伴们学习第三章链表应该会轻松一点吧 目录 类模板下的单链表 1.1书上干净完整代码&#xff08;无增改、适合自己动手实验&#xff09; 1.2对书上代码的完善和对一些问题的验证和解释代码 1.补全一个函数&#xff1a; 2.this指…

荧光染料IR 825叠氮IR825 N3,IR-825 azide,IR-825叠氮 科研试剂

产品描述&#xff1a;IR-825 N3含有叠氮基团&#xff0c;IR-825是一种近红外染料&#xff08;NIR&#xff09;&#xff0c;IR-825在封装成纳米颗粒后&#xff0c;可能用于cancer光热和光动力 。叠氮化物基团可以参与铜催化的与炔部分的点击化学反应。西安凯新生物科技有限公司近…

基于多任务融合的圣女果采摘识别算法研究

基于多任务融合的圣女果采摘识别算法研究 1、简介 本文主要解决圣女果生产销售环节中&#xff0c;现有的流程是采摘成熟的圣女果&#xff0c;再对采摘下的果实进行单独的品质分级&#xff0c;不仅费时费力&#xff0c;而且多增加一个环节&#xff0c;也增加了对果实的二次伤害…

Oracle 19c之RPM安装

19c的RPM包下载链接&#xff0c; https://www.oracle.com/database/technologies/oracle19c-linux-downloads.html 可以看到&#xff0c;19c开始支持企业版本的RPM&#xff0c;容量是2.5GB&#xff0c; 使用手工方式&#xff0c;通过RPM安装19c数据库&#xff0c;只需要两步操…

汽车零部件行业MES解决方案,实现生产全过程监控

行业背景 汽车汽配行业是中国国民经济的支柱产业&#xff0c;涉及的工艺包括压铸、冲压、注塑、机加、焊接、电子、喷涂、电镀、热处理、检测、装配等。 公安部数据显示&#xff0c;平均每百户家庭拥有汽车达到60辆。广阔的市场为行业带来大量需求的同时也带来了激烈的市场竞…

【Linux】网络入门

&#x1f387;Linux&#xff1a; 博客主页&#xff1a;一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持…

栈和队列详细讲解+算法动画

栈和队列 栈stack 栈也是一种线性结构相比数组&#xff0c;栈对应的操作数数组的子集只能从一端添加元素&#xff0c;也只能从一端取出元素这一端称为栈顶 栈是一种后进先出的数据结构Last in Firt out(LIFO)在计算机的世界里&#xff0c;栈拥有者不可思议的作用 栈的应用 …

设计UI - Adobe xd对象介绍

矩形工具 新建矩形 操作步骤&#xff1a;选择矩形工具&#xff0c;快捷键R&#xff0c;鼠标在画板上拖出矩形即可。 拖动定界框周围圆形手柄&#xff0c;可快速调整矩形大小&#xff0c;也可以输入宽和高的参数对矩形大小进行改变。 移动矩形 操作步骤&#xff1a;选择选择工具…

AWS-解析mysql binlog同步数据方案

虽然是公有云的鼻祖&#xff0c;AWS在某些产品的实现却太不给力&#xff1b;可能是习惯了阿里云喂到嘴边的感觉&#xff0c;AWS很多方案需要自己折腾&#xff0c;蛋疼&#xff01;比如这里要讲的mysql数据同步方案。阿里云产品DTS&#xff0c;点几下就OK了&#xff0c;AWS&…

06_01_Spark SQL

Spark SQL 课程目标 说出Spark Sql的相关概念说出DataFrame与RDD的联系独立实现Spark Sql对JSON数据的处理独立实现Spark Sql进行数据清洗 1、Spark SQL 概述 Spark SQL概念 Spark SQL is Apache Spark’s module for working with structured data. 它是spark中用于处理结…

百家号如何写文章赚钱,百家号写文章真的赚钱?

随着互联网的快速发展&#xff0c;越来越多的人开始关注到写文章赚钱这个领域。而在众多写作平台中&#xff0c;头条号无疑是最受欢迎的一个。那么&#xff0c;百家号写文章赚钱是真的吗&#xff1f;如何写文章赚钱呢&#xff1f;下面我们就来一一解答。 首先&#xff0c;百家号…

Javascript的ES6 class写法和ES5闭包写法性能对比

看到很多闭包写法的函数, 一直怀疑它对性能是否有影响. 还有就是备受推崇的React Hooks函数式写法中出现大量的闭包和临时函数, 我很担心这样会影响性能. 于是, 做了一个实验来做对比. 这个实验很简单, 用md5计算一百万次. 计算过程将结果再放回参数, 这样避免结果没被引用被…

Git 学习(三)—— 本地仓库 — 远程仓库的操作命令

为了可以让其他用户看到自己的成果&#xff0c;我们可以将自己本地仓库的内容上传到远程仓库&#xff1b;如果我们希望借鉴其他用户的成果&#xff0c;我们可以将远程仓库里的一些内容拉取或者克隆到本地仓库。 这里先暂不考虑 本地到远程 或者 远程到本地 的一些相关操作&…

避坑指南—GPL开源协议

0x00 前言 本文主要目的是为了了解一些基础的GPL注意事项&#xff0c;以及防止被一些一知半解的人蒙骗。本文不做任何内容的依据&#xff0c;仅为个人见解&#xff0c;仅供参考。 一些常见的开源协议 GPLBSDMITMozillaApacheLGPL 0x01 GPL GPL许可协议(GNU General Public …