Article directory
MyBatis
1. What is MyBatis?
MyBatis is a data persistence layer framework that supports custom SQL, stored procedures (rarely used) and advanced mapping. MyBatis removes almost all JDBC code and the work of setting parameters and obtaining result sets. MyBatis can configure and map primitive types, interfaces, and Java POJOs (Plain Old Java Objects, Plain Old Java Objects) as records in the database through simple XML or annotations.
To put it simply, MyBatista is a tool for simply completing program and data interaction, that is, a tool for simpler operation and reading of databases.
2. Why learn MyBatis
For Java back-end programmers, the program is nothing more than two parts
- program
- database
Back-end programmers are nothing more than CRUD of the database.
For these two important components to communicate, they must rely on database connection tools, typically JDBC. Why do we need to learn the MyBatis framework when we have JDBC? JDBC probably has several processes:
- Go to the Maven repository to download the JDBC jar package and import it
- Create a database, set the user name and password in the code create a DataSource to get the Connection connection
?
Write SQL statements with placeholders- Replace the placeholder
?
with the name of the field to be operated and specify the type - Obtain the Statement object through the connection
- Execute SQL through Statement to obtain ResultSet result set or affect the number of rows
- Process the result set
- release resources
Every time JDBC operates the database, it needs to repeat the operation again and again, establish a connection, assemble SQL, execute SQL, process the result set, and finally release resources.
In order to solve this series of troublesome operations, you can use MyBatis to operate the database more simply.
3. Build the first MyBatis environment
This is a simple framework interaction flowchart
- Mapper is what we call the data persistence layer
- Inteface is the interface we write in the class. The methods in the interface are not specifically implemented. An interface corresponds to a table. The methods in the table correspond to some operations
- In xml, it is the implementation of the interface method, the writing of SQL, and xml is the fixed writing method provided by MyBatis
- When the program is running, MyBatis will combine Inteface and xml into one, generate SQL and call JDBC
- Then return the result from the Mapper to the Service layer
MyBatis is also an ORM framework, ORM (Object Relational Mapping), that is, object-relational mapping. In the object-oriented programming language, the data objects in the relational database establish a mapping relationship, and then automatically complete the conversion between data and objects:
- Map input data (that is, incoming objects) + SQL to native SQL
- Map the result set back to the object, which is the output object
The ORM maps the database to objects:
- Database table (table) --> class (class)
- Record (record, row data) --> Object (Object)
- field (field) --> object's attribute (attribute)
A general ORM framework maps each table of the database model to a Java class. That is to say, using MyBatis can operate data tables like operating objects, and can realize the conversion between objects and databases.
1) Add MyBatis framework support
Add MyBatis to old projects
References to both frameworks need to be added
- MyBatis framework
- MySQL driver
2) Configure MyBatis related configuration files
1. Configure data connection information
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/blog_system?characterEncoding=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver #mysql 8.0之前的写法
#driver-class-name=com.mysql.cj.jdbc.Driver 这是8.0之后的写法
2. MyBatis XML file location
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/blog_system?characterEncoding=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver #mysql 8.0之前的写法
#driver-class-name=com.mysql.cj.jdbc.Driver 这是8.0之后的写法
mybatis:
mapper-locations: classpath:mapper/**Mapper.xml
The location of the xml file in the configuration file should correspond to the name of the created folder
3) Add code
UserInfo object
Note: The attribute name here should correspond to the field name in the database
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class UserInfo {
private int userId;
private String username;
private String password;
}
UserMapper.xml
document
<?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.mapper.UserMapper">
<select id="getAll" resultType="com.example.demo.model.UserInfo">
select * from user
</select>
</mapper>
controller code
@Controller
@RequestMapping("/mybatis")
@ResponseBody
public class UserController2 {
@Autowired
private UserService userService;
@RequestMapping("/getAll")
public List<UserInfo> getAll() {
return userService.getAll();
}
}
Service code
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public List<UserInfo> getAll() {
return userMapper.getAll();
}
}
define interface code
@Mapper
public interface UserMapper {
// 方法定义
List<UserInfo> getAll();
}
Access via browser
4. Solve the inconsistency between the attribute name of the class and the field name of the data table (resultMap)
Entity class
@Getter
@Setter
public class UserInfo {
private int id;
private String name;
private String password;
}
data sheet
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| userId | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(64) | YES | UNI | NULL | |
| password | varchar(32) | YES | | NULL | |
+----------+-------------+------+-----+---------+----------------+
MyBatis configuration file
<?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.mapper.UserMapper">
<resultMap id="UserBean" type="com.example.demo.model.UserInfo">
<!-- 映射主键的(表中主键和程序实体类中的主键) -->
<id column="userId" property="id"></id>
<!-- 普通列的映射 -->
<result column="username" property="name"></result>
<result column="password" property="password"></result>
</resultMap>
<select id="getAll" resultMap="UserBean">
select * from user
</select>
</mapper>
To resultMap
solve the inconsistency between the attribute name of the class and the field name in the data table
id
label represents the primary keyresult
Labels represent common fields
MyBatis query return type settings
- resultType (return result type)
- resultMap (return map)
resultType
VSresultMap
- What they have in common: their functions are the same, and they are all used to specify the result type
- difference:
resultType
The usage is simple, but if the attribute name in the entity class is inconsistent with the field name in the table, the result will not be queriedresultMap
The usage is relatively troublesome (resultMap interface has multiple columns), but it can realize the mapping that does not correspond to the attribute name and the field name of the data table, and can also query the result
5. Add operations
1) Return the number of affected rows
Controller (controller) code:
@Controller
@RequestMapping("/mybatis")
@ResponseBody
public class UserController2 {
@Autowired
private UserService userService;
public Integer addUser(UserInfo userInfo) {
// 校验参数
if (userInfo == null || userInfo.getName() == null || "".equals(userInfo.getName())
|| userInfo.getPassword() == null || "".equals(userInfo.getPassword())) {
return 0;
}
return userService.addUser(userInfo);
}
}
Service (service layer code):
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public int addUser(UserInfo userInfo) {
// 服务(方法编排)
return userMapper.addUser(userInfo);
}
}
Mapper interface code:
@Mapper
public interface UserMapper {
int addUser(UserInfo userInfo);
}
Write the xml configuration file of MyBatis
<?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.mapper.UserMapper">
<!-- 添加方法 -->
<insert id="addUser">
insert into user(username,password) values(#{name},#{password})
</insert>
</mapper>
Tested by postman
2) Return the self-incrementing id
Controller (controller) code:
@Controller
@RequestMapping("/mybatis")
@ResponseBody
public class UserController2 {
@Autowired
private UserService userService;
@RequestMapping("/add2")
public Integer addUser2(UserInfo userInfo) {
// 校验参数
if (userInfo == null || userInfo.getName() == null || "".equals(userInfo.getName())
|| userInfo.getPassword() == null || "".equals(userInfo.getPassword())) {
return 0;
}
// 调用数据库执行添加操作,执行完添加之后会将自增id设置到userinfo的id属性
userService.addUser2(userInfo);
return userInfo.getId();
}
}
Service (service layer code):
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public int addUser2(UserInfo userInfo) {
// 服务(方法编排)
return userMapper.addUser2(userInfo);
}
}
Mapper interface:
@Mapper
public interface UserMapper {
int addUser2(UserInfo userInfo);
}
Write the xml configuration file of MyBatis
<?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.mapper.UserMapper">
<!-- 添加方法(返回自增id) -->
<insert id="addUser2" useGeneratedKeys="true" keyProperty="id" keyColumn="userId">
insert into user(username, password) values(#{name},#{password})
</insert>
</mapper>
- useGeneratedKeys : This will make MyBatis use JDBC's getGeneratedKeys method to retrieve
the primary key generated internally by the database (for example: auto-
increment ), default value: false - keyColumn
: Set the name of the column in the table that generates the key value. In some databases (like PostgreSQL), it must be set when the primary key column is not the first column in the table. If there is more than one generated column, multiple attribute
names can be separated by commas - keyProperty : Specifies the property that can uniquely identify the object. MyBatis will use the return value of getGeneratedKeys
or the selectKey subelement of the insert statement to set its value. Default value: unset (unset).
If there is more than one generated column , multiple attribute names can be separated by commas
Postman test results
6. Modify operation
Controller (controller code)
@Controller
@RequestMapping("/mybatis")
@ResponseBody
public class UserController2 {
@Autowired
private UserService userService;
/**
* 修改密码操作
* @param id
* @param password
* @return
*/
@RequestMapping(value = "/update")
public Integer updateUserInfo(Integer id, String password) {
if(id == null || id < 0 || password == null || "".equals(password)) {
return 0;
}
return userService.updateUserInfo(id,password);
}
}
Service service layer code:
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public int updateUserInfo(Integer id, String password) {
return userMapper.updateUserInfo(id,password);
}
}
Mapper interface method definition
@Mapper
public interface UserMapper {
int updateUserInfo(Integer id, String password);
}
MyBatis configuration file implements the interface
<?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.mapper.UserMapper">
<!-- 更新操作 -->
<update id="updateUserInfo">
update user set password=#{password} where userId=#{id}
</update>
</mapper>
7. Delete operation
Controller controller code:
@Controller
@RequestMapping("/mybatis")
@ResponseBody
public class UserController2 {
@Autowired
private UserService userService;
@RequestMapping("/delete")
public Integer deleteUser(Integer id) {
if (id == null || id < 0) {
return 0;
}
return userService.deleteUser(id);
}
}
Service service layer code
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public int deleteUser(int id) {
return userMapper.deleteUser(id);
}
}
Mapper interface method definition
@Mapper
public interface UserMapper {
int deleteUser(int id);
}
MyBatisj configuration file interface implementation
<?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.mapper.UserMapper">
<!-- 删除操作 -->
<delete id="deleteUser">
delete from user where userId=#{id}
</delete>
</mapper>
8. Two ways of parameter assignment
#{}
: precompiled processing${}
: character direct replacement
Precompilation processing means: MyBatis will replace the number #{}
in the SQL with the number when processing , and use the set method of PreparedStatement to assign the value. Direct replacement: When MyBatis is processing , it is directly replaced with the value of the variable.#{}
?
${}
${}
${}
scenes to be used:
Suppose we want to sort users in descending order by id, and the variable order is passed asc or desc
@Mapper
public interface UserMapper {
// 方法定义
List<UserInfo> sortAllUser(String order);
}
<!-- ${}使用场景 -->
<select id="sortAllUser" resultMap="UserBean">
select * from user order by userId ${order}
</select>
Assuming that desc is passed , if ${}
it is used, ${order}
it will be replaced directly desc
, and the result will be returned in normal sorting. And if #{}
it is used, it will be replaced with ‘desc’
, 'desc'
which is a string at this time, and an error will occur when executing SQL.
-- 使用${}
select * from user order by userId desc;
-- 使用#{}
select * from user order by userId 'desc';
But${}
there is a problem of SQL injection
SQL injection problem
Suppose to do a simple login
Controller controller code
@Controller
@RequestMapping("/mybatis")
@ResponseBody
public class UserController2 {
@Autowired
private UserService userService;
/**
* 登录
* @param username
* @param password
* @param request
* @return
*/
@RequestMapping("/login")
public Object logIn(String username, String password, HttpServletRequest request) {
Map<String,Object> result = new HashMap<>();
result.put("status",200);// 响应状态码
String message = null;
int state = -1; // 登录状态码
if (username != null && !"".equals(username) && password != null && !"".equals(password)) {
UserInfo userInfo = userService.logIn(username,password);
if (userInfo != null) {
message = "登录成功";
state = 1;
// 把用户信息存储到session
HttpSession session = request.getSession(true);
session.setAttribute("userInfo",userInfo);
} else {
message = "用户名或密码错误";
}
} else {
message = "用户名或密码错误";
}
result.put("message",message);
result.put("state",state);
return result;
}
}
UserService service layer code
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public UserInfo logIn(String username, String password) {
return userMapper.logIn(username,password);
}
}
Mapper interface mapping
@Mapper
public interface UserMapper {
// 方法定义
List<UserInfo> getAll();
UserInfo logIn(String username, String password);
}
The xml configuration file that implements the interface
<!-- 登录用户查询 -->
<select id="logIn" resultMap="UserBean">
select * from user where username='${username}' and password='${password}'
</select>
If you use ${}
the method to directly replace the parameters, there will be a risk of SQL injection
Postman mock request
http://127.0.0.1:8080/mybatis/login?username=admin&password=' or userId='1''
No matter what password you enter, you can log in successfully. The final SQL executed is as follows. That is to say, you can log in without knowing the password through SQL injection, and even perform table deletion operations.
mysql> select * from user where username='admin' and password='' or userId='1';
If you ${}
replace #{}
the achievement, there will be no SQL injection problem
<select id="logIn" resultMap="UserBean">
select * from user where username=#{username} and password=#{password}
</select>
If it is used , it will replace the number #{}
in SQL with the number, and use the set method of PreparedStatement to assign the value. Think of the whole as a string.#{}
?
The SQL executed using the SQL injection just now will be as follows
mysql> select * from user where username="admin" and password="' or userId='1";
Therefore, ${}
you must use it with caution. If you must use it, you must filter the front-end parameters for security .
Safe fuzzy query (like)
ike uses #{} to report an error
This is an example of an error
<!-- 过滤查询 -->
<select id="getLike" resultMap="UserBean">
select username from user where username like '#{keyName}%';
</select>
The SQL equivalent to execute is
select username from user where username like '"张"%';
This is not possible to use ${} directly, you can consider using mysql's built-in function concat() to deal with, the implementation code is as follows:
<!-- 过滤查询 -->
<select id="getLike" resultMap="UserBean">
select username from user where username like concat(#{keyName},'%')
</select>