目录
一、准备工作
首先我们搭建好 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>
useGeneratedKeys="true"
:这个属性用于指示MyBatis使用JDBC的getGeneratedKeys方法来获取数据库自动生成的主键值。当数据库表配置了自增主键(如MySQL的AUTO_INCREMENT)或其他类型的自动生成主键机制时,这个属性非常有用。设置为
true
意味着MyBatis将尝试获取由数据库生成的主键值,并将其映射回Java对象。
keyColumn="id"
:这个属性指定了数据库中的哪个列是主键列。在这个例子中,
id
是主键列的名称。这个属性告诉MyBatis在获取生成的键值时要查找哪个数据库列。注意,这里的列名应该与数据库中的实际列名相匹配。
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 进行增删改查的一个基础操作,希望大家有所收获。