一、MyBatis框架简介
MyBatis是一个持久化框架,支持自定义SQL、存储过程以及高级映射,是一个优秀的ORM(对象关系映射)的框架。
MyBatis框架的特点就是比较灵活。
二、创建Mybatis项目
1、在Mysql中创建要进行操作的数据库和数据表。
2、创建项目并添加MyBatis依赖。
#配置数据库的连接信息 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver
3、配置MyBatis连接字符串以及保存的xml目录。
配置数据库连接信息:
#配置数据库的连接信息 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver
xml文件保存路径:
#设置MyBatis的xml保存路径 mybatis: mapper-locations: classpath:mybatis/**Mapper.xml
4、添加业务代码
a、创建model包,添加UserInfo实体类:
@Data public class UserInfo { private Integer id; private String username; private String password; private String photo; private String createTime; private String updateTime; private int state; }
b、创建mapper包,定义UserMapper接口:
@Mapper public interface UserMapper { public UserInfo getUserInfoById(@Param("id") Integer id); }
在resources目录下创建mybatis文件夹,创建UserMapper.xml文件,文件内容如下,其中select标签的id表示要执行的方法名,resultType表示返回的类的路径:
<?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.mybatisdemo.mapper.UserMapper"> <select id="getUserInfoById" resultType="com.example.mybatisdemo.model.UserInfo"> select * from userinfo where id=${id} </select> </mapper>
创建service包,创建UserService类,获取到UserMapper并实现getUserInfoById方法:
@Service public class UserService { @Resource public UserMapper userMapper; public UserInfo getUserInfoById(Integer id){ return userMapper.getUserInfoById(id); } }
创建controller包,创建UserController类,获取到UserService类并实现getUserInfoById方法:
@Controller public class UserController { @Autowired private UserService userService; @ResponseBody @RequestMapping("/getuserinbyid") public UserInfo getUserInfoById(Integer id){ if(id == null){ return null; } return userService.getUserInfoById(id); } }
访问验证:
三、利用MyBatis操作数据库
在操作数据库之前,首先了解一下单元测试,单元测试可以针对某一方法进行测试,并且不需要再启动SpringBoot项目,使用单元测试有如下好处:
- 使用单元测试可以非常直观、简单地测试某一功能的正确性;
- 使用单元测试可以帮助在打包的时候发现一些错误,因为在打包之前要求所有的单元测试都必须通过。
- 使用单元测试不会对原有的数据库造成污染。
如果不想对数据库造成污染,就需要添加@Transactional注解,其原理是先执行sql语句,在进行回滚操作。
使用单元测试的一般步骤:
先生成单元测试的类:
在接口中右击鼠标,单击generate:
选择test,出现如下界面:
点击ok之后就会自动生成测试类。
增加数据
增加一条用户信息,并返回受影响的行数。
首先需要在UserMapper接口中声明方法。
//添加用户信息,返回受影响的行数 public int addUserInfo(UserInfo userInfo);
然后需要在resources文件夹下的MyBatis文件下的UserMapper.xml文件中新增如下的insert标签:
<!--增加用户信息,返回受影响的行数 --> <insert id="addUserInfo"> insert into userinfo(id,username,password,photo) values(#{id},#{username},#{password},#{photo}) </insert>
在生成的测试类中编写代码:
@Test void addUserInfo() { UserInfo userInfo = new UserInfo(); userInfo.setId(2); userInfo.setUsername("lucy"); userInfo.setPassword("1234"); userInfo.setPhoto("test.jpg"); int i =userMapper.addUserInfo(userInfo); Assertions.assertEquals(1,i);//使用断言测试返回受影响的行数是否为1 }
启动单元测试的结果:
由于当时在新建数据表时id是默认自增的,就需要在xml文件中的 insert添加如下属性:
<!--增加用户信息,返回受影响的行数 如下参数表示id为自增的--> <insert id="addUserInfo" useGeneratedKeys="true" keyProperty="id" keyColumn="id"> insert into userinfo(username,password,photo) values(#{username},#{password},#{photo}) </insert>
删除数据
删除指定id的用户信息,并返回受影响的行数。
在UserMapper接口中定义删除数据的方法:
@Transactional //删除用户信息不污染数据库 //根据ID查询User信息 public int deleteUserInfo(@Param("id") Integer id);
在xml文件下添加delete标签:
<!-- 通过指定id删除用户信息 --> <delete id="deleteUserInfo"> delete from userinfo where id=#{id} </delete>
在测试类中编写代码:
//根据ID删除UserInfo信息 @Test void deleteUserInfo() { int i =userMapper.deleteUserInfo(3); Assertions.assertEquals(1,i);//使用断言测试返回受影响的行数是否为1 }
启动单元测试运行结果:
修改数据
修改指定id的用户名。
在UserMapper接口中定义修改数据方法:
//根据id修改用户名 public int updateUserInfo(@Param("id") Integer id,@Param("username") String username);
在xml文件中添加update标签:
<!--指定id修改用户名--> <update id="updateUserInfo"> update userinfo set username=#{username} where id=${id} </update>
在生成的测试类中编写测试代码:
//根据id修改用户名 @Test void updateUserInfo() { int i = userMapper.updateUserInfo(2,"mary"); Assertions.assertEquals(1,i);//使用断言测试返回受影响的行数是否为1 }
测试运行结果:
查询数据
根据id查询用户信息。
在UserMapper中定义查询方法:
@Transactional //根据id查询UserInfo信息 public UserInfo getUserInfoById(@Param("id") Integer id);
在xml文件中添加select标签,其中resultType标签表示返回的对象所属类的路径
<!-- 通过id查询用户信息 --> <select id="getUserInfoById" resultType="com.example.mybatisdemo.model.UserInfo"> select * from userinfo where id=${id} </select>
在测试类中编写测试代码:
//通过id查询用户信息 @Test void getUserInfoById() { UserInfo userInfo = userMapper.getUserInfoById(1); Assertions.assertNotNull(userInfo); }
测试运行结果:
在之前的xml文件中使用${}和#{} 这两种参数占位符,那么这两种占位符有如下区别:
定义不同:${}直接替换,#{}会进行预处理;
用法不同:${}只适用于整数替换,#{}适合所有的数据类型;
安全性不同:${}会导致sql注入安全问题,#{}的性能高,并且不存在安全问题。
${}如何会引起sql注入呢?
在userinfo信息表中有username和password两个属性,那么在查询的时候给password属性传入‘or 1= ‘1’’就会导致查出所有的用户信息包含用户的密码:
xml文件中新增如下的select标签:
<!-- 通过用户名和密码查询用户信息 --> <select id="getUserInfoByNameAndPwd" resultType="com.example.mybatisdemo.model.UserInfo"> select * from userinfo where username='${username}' and password=${password} </select>
使用如下测试代码:
//根据用户名和密码查询用户信息 @Test void getUserInfoByNameAndPwd() { String username = "admin"; String password = "''or 1 ='1'"; userMapper.getUserInfoByNameAndPwd(username,password); }
查询到了所有的userinfo导致sql注入问题:
但是将参数占位符换为#{},再进行测试:
#{}占位符会进行预处理,不会直接进行替换,就不会出现sql注入的问题。
那么?{}就没有应用场景了吗?答案当然是否定的,任何东西存在即合理,当传递的参数是sql关键字或sql命令时就需要使用?{}占位符,因为需要直接替换,不需要#{}来进行预处理为字符串,否则就会报错。
利用MyBatis框架操作数据库2:https://developer.aliyun.com/article/1521802?spm=a2c6h.13148508.setting.18.55b44f0eiXO8eW