【MyBatis】MyBatis如何增删改查

目录

一、准备工作

二、MyBatis的查操作

2.1 查询所有信息

2.2 查询单个信息

2.3 sql注入问题

2.4 like查询

2.5 @Select注解查询

三、MyBatis的增、删、改操作

3.1 增操作

3.2.1 获取新增数据的id

3.2 删除操作

3.3 改操作 

3.4 属性名不一致问题

3.4.1 起别名

3.4.2 手动映射


一、准备工作

首先我们搭建好 MyBatis 项目以及准备好一张 UserInfo 表,通过这张表来演示增删改查的过程。MyBatis 项目的搭建在上一篇博客中已讲解,即MyBatis的创建以及启动。下面是表内容并存在于 MySQL 数据库中。

mysql> create table userinfo(
    -> id int primary key auto_increment,
    -> username varchar(20) not null,
    -> password varchar(32) not null);
Query OK, 0 rows affected (0.02 sec)

mysql> insert into userinfo(id,username,password) values
    -> (1,"zhangsan","123"),
    -> (2,"lisi","456");
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | zhangsan | 123      |
|  2 | lisi     | 456      |
+----+----------+----------+

此外还需进行在你的项目中添加以下操作:

创建一个 dao 文件夹,在该文件夹中创建一个接口在此我命名为 UserMapper 这个接口的作用就是添加查询方法,如查询所有信息此时就可以在该接口中添加一个 getAll() 方法,后续我们通过该接口的 xml 文件来实现 getAll() 方法对应的 sql 语句。

@Mapper
public interface UserMapper {
    //查询全部信息
    List<UserInfo> getAll();
    //查询单个信息
    UserInfo  getUserById(@Param("id") Integer uid);
    //等操作...
}

创建一个 model 文件夹,在该文件中创建一个 UserInfo 作为一个被操作的对象,该对象的内容应与 mysql 数据库中的 userinfo 表属性保持一致。

@Component
public class UserInfo {
    private int id;
    private String username;
    private String password;
}


resours 文件下创建一个 UserMapper.xml 文件用来执行 UserMapper 接口对应方法的 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="com.example.demo.dao.UserMapper">
</mapper>

在上述的 namespace 路径中,我们可以看到关联到的就是 UserMapper 接口,因此我们可以在 xml 文件中来执行 UserMapper 接口中的增删改查方法。

在准备好上述的操作后我们就可以通过 MyBatis 来进行对表进行增删改查了。


二、MyBatis的查操作

2.1 查询所有信息

xml文件:

<?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="com.example.demo.dao.UserMapper">
        <select id="getAll" resultType="com.example.demo.model.UserInfo">
                select * from userinfo
        </select>
</mapper>

xml 文件中,<select>标签对应的就是查询操作,其中 id 对应的是接口中的方法名,resultType 对应的是返回到哪个类。 

接口文件: 

@Mapper
public interface UserMapper {
    //查询全部信息
    List<UserInfo> getAll();
}

测试文件: 

    @Test //查询所有信息
    void getAll() {
        List<UserInfo> list = userMapper.getAll();
        System.out.println(list);
    }

运行结果: 


2.2 查询单个信息

接口代码:

@Mapper
public interface UserMapper {
    //查询单个信息
    UserInfo  getUserById(@Param("id") Integer uid);
}

 @Param 注解作为方法的参数,在 xml 文件中 #{}${} 中对应的就是该参数。#{} ${} 都可替换对应的参数但通常使用 #{} ,具体原因在下方会讲解。

xml代码:

<?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="com.example.demo.dao.UserMapper">
        <select id="getUserById" resultType="com.example.demo.model.UserInfo">
                select * from userinfo where id=#{id}
        </select>
</mapper>

测试代码 :

@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void getUserById() {
        UserInfo userInfo = userMapper.getUserById(1);
        System.out.println(userInfo.toString());
    }

}

运行结果: 


2.3 sql注入问题

sql 注入即使用一些特定手段来完成一些非法的操作,如一个登录操作,正常的流程是输入账号、密码登录成功,而不法分子通过特殊的 sql 语句来只输入账号来获取你的密码。

首先我们要了解到:

  • ${}是及时执行,不安全,${}会直接替对应的值,它会在 MyBatis 处理 SQL 语句之前,将参数值直接插入到 SQL 语句中。
  • #{}是预执行,安全 ,#{}不会直接替换除“数值”以外的值,它会在 MyBatis 处理 SQL 语句时,使用 JDBC 的 PreparedStatement 来设置参数值。

假设以下为一个登录操作:

    //登录操作
    UserInfo login(@Param("username") String username,@Param("password") String password);
        <select id="login" resultType="com.example.demo.model.UserInfo" >
                select * from userinfo where username=${'username'} and password=${'password'}
        </select>
    @Test
    void login() {
        String username = "zhangsan";
        String password = "' or 1='1";
        UserInfo userInfo = userMapper.login(username,password);
        System.out.println(userInfo.toString());
    }

通过上述例子我们可以发现,当我定义特定的字符串为密码时,也输出了正确的密码,这就是一个简单的 sql 注入。那么又有一个问题,为什么 ${} 能做的事情 #{} 也能做,既然 ${} 存在 sql 注入问题为啥不直接使用 #{} 呢? 

通过上文解释我们知道了,${} 除数值外的数据都不能直接替换,因此推荐使用 #{} ,但某个模块还是得用 ${} 。如购物平台的价格从高到底或从低到高一栏,假设需要从高到低则 sql 语句得使用desc ,此时使用 #{} 则会使 desc 变为 'desc' ,因此某些模块还是得使用 ${}

因此,当业务代码需要使用 sql 语句时则使用 ${},换个说法当使用场景能被穷举如上述的升序或降序操作,例如以下代码:

        <select id="getAllByOrder" resultType="com.example.demo.model.UserInfo">
                select * from userinfo order by id ${myOrder}
        </select>

    List<UserInfo> getAllByOrder(@Param("myOrder") String myOrder);
    @Test
    void getAllByOrder() {
        List<UserInfo> list = userMapper.getAllByOrder("desc");
        System.out.println(list);
    }


2.4 like查询

    //like查询
    List<UserInfo> getLikeList(@Param("username") String username );
        <select id="getLikeList" resultType="com.example.demo.model.UserInfo">
                select * from userinfo where username like concat('%',#{username},'%')
        </select>

在上述的 sql 注入问题中我们了解到了,${} 只适用于能够被穷举的案例如升序、降序。而 #{} 又不能直接替换 like 查询中的 %**%,因此可以使用 concat 来连接,如上述代码。

    @Test
    void getLikeList() {
        String username = "lisi";
        List<UserInfo> list = userMapper.getLikeList(username);
        System.out.println(list);
    }


2.5 @Select注解查询

mysql 数据库中新增一个文章表,文章表中应有 username userinfo 表中的 username 进行相关联。在 mybatis 项目也创建对应的实体类和接口,如下所示:

mysql> create table articleinfo(
    -> id int,
    -> title varchar(20),
    -> content varchar(50),
    -> username varchar(30)
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql> insert into articleinfo(id,title,content,username) values
    -> (1,"t1","abc","wangwu"),
    -> (2,"t2","def","lisi");
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from articleinfo;
+------+-------+---------+----------+
| id   | title | content | username |
+------+-------+---------+----------+
|    1 | t1    | abc     | wangwu   |
|    2 | t2    | def     | lisi     |
+------+-------+---------+----------+
2 rows in set (0.00 sec)

 在上文的查操作我们都使用的是通过 xml 文件来执行 sql 语句,还有一种方式是在接口中直接使用 @Select() 注解来完成对 sql 语句的操作,如下代码:

@Mapper
public interface ArticleMapper {

    @Select("select * from articleinfo")
    List<ArticleInfo> getAll();
    
}
@SpringBootTest
class ArticleMapperTest {

    @Autowired
    private ArticleMapper articleMapper;

    @Test
    void getAll() {
        List<ArticleInfo> list = articleMapper.getAll();
        System.out.println(list);
    }
}

以上操作是查询 articleinfo 表中的所有信息,省略了在 xml 文件中使用 <select></select> 标签并添加 id 、返回类型、sql 语句。直接在接口方法上方使用 @Select 注解就能完成相应的操作。 


三、MyBatis的增、删、改操作

3.1 增操作

    //添加操作
    int add(UserInfo userInfo);
        <insert id="add">
                insert into userinfo(username,password) values (#{username},#{password})
        </insert>
    @Test
    void add() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("Bob");
        userInfo.setPassword("789");
        int ret = userMapper.insert(userInfo);
        System.out.println("增添操作:"+ret);
    }


3.2.1 获取新增数据的id

    //获取新增数据的id
    int insert(UserInfo userInfo);
        <insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
                insert into userinfo(username,password) values (#{username},#{password})
        </insert>
  1. useGeneratedKeys="true"

    这个属性用于指示MyBatis使用JDBC的getGeneratedKeys方法来获取数据库自动生成的主键值。当数据库表配置了自增主键(如MySQL的AUTO_INCREMENT)或其他类型的自动生成主键机制时,这个属性非常有用。设置为true意味着MyBatis将尝试获取由数据库生成的主键值,并将其映射回Java对象。

  2. keyColumn="id"

    这个属性指定了数据库中的哪个列是主键列。在这个例子中,id是主键列的名称。这个属性告诉MyBatis在获取生成的键值时要查找哪个数据库列。注意,这里的列名应该与数据库中的实际列名相匹配。

  3. keyProperty="id"

    这个属性指定了Java对象中的哪个属性应该被设置为生成的主键值。在这个例子中,id是Java对象中的一个属性名。这个属性告诉MyBatis将获取到的生成主键值设置到Java对象的哪个属性上。这样,当MyBatis执行插入操作后,它就可以将生成的主键值自动填充到Java对象的id属性中。

    @Test
    void insert() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("Ailisi");
        userInfo.setPassword("999");
        int ret = userMapper.insert(userInfo);
        System.out.println("新增数据:"+ret+"条"+",新增数据的id为:"+userInfo.getId());
    }


3.2 删除操作

    //删除操作
    int deleteById(@Param("id") Integer id);
        <delete id="deleteById">
                delete from userinfo where id=#{id}
        </delete>
    @Transactional
    @Test
    void deleteById() {
        int id = 2;
        int ret = userMapper.deleteById(id);
        System.out.println("删除的行数为:"+ret);
    }

在单元测试中使用 @Transactional 注解会将执行的操作回滚,即不会影响原数据只是进行一个测试。


3.3 改操作 

    //修改操作
    int upDate(UserInfo userInfo);
        <update id="upDate">
                update userinfo set username=#{username} where id=#{id}
        </update>
    @Test
    void upDate() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1);
        userInfo.setUsername("wangwu");
        int ret = userMapper.upDate(userInfo);
        System.out.println("修改的行数为:"+ret);
    }


3.4 属性名不一致问题

属性名不一致:即需要查询的数据属性名与类中的属性名不一致时解决方法。


3.4.1 起别名

直接将类中对应的值修改为被查询数据的名即可(这是最简便的方法),或使用 as 起别名的方式使查询时与表中属性名相同。

        <select id="getAllByOrder" resultType="com.example.demo.model.UserInfo">
                select id,username as name,password from userinfo order by id ${myOrder}
        </select>

3.4.2 手动映射

在MyBatis 中,当数据库表的列名与 Java 对象的属性名不一致时,可以通过手动映射来解决这个问题。MyBatis 提供了多种方式来实现这种映射,包括在 XML 映射文件中使用 <resultMap> 元素,或者在注解中使用 @Results 和 @Result 注解。

<resultMap id="userResultMap" type="com.example.User">
    <result property="userId" column="id"/>
    <result property="userName" column="username"/>
    <result property="userEmail" column="email"/>
    <!-- 其他列与属性的映射 -->
</resultMap>

<select id="selectUserById" resultMap="userResultMap">
    SELECT id, username, email FROM userinfo WHERE id = #{id}
</select>

在这个例子中,userResultMap 是一个结果映射,它指定了 id 列映射到 User 对象的 userId属性,username 列映射到 userName 属性,以及 email列映射到 userEmail 属性。 


以上就是 MyBatis 进行增删改查的一个基础操作,希望大家有所收获。

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一只爱打拳的程序猿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值