MyBatis

传统JDBC与MyBatis

在Java应用程序中,数据库操作是一个重要的组成部分。传统JDBC(Java Database Connectivity)是Java提供的一种标准API,用于与关系型数据库进行交互。然而,传统JDBC存在一些不足之处,如代码冗余、手动管理数据库连接等。MyBatis作为一种持久层框架,提供了更灵活、高效的数据库操作方式。以下是MyBatis相较于传统JDBC的优点:

  1. 基于SQL语句编程,相当灵活

MyBatis允许开发者直接编写SQL语句,并将SQL语句写在XML文件中。这种方式使得SQL语句的管理和维护更加方便,同时也支持编写动态SQL,提高了SQL语句的可重用性。

  1. 减少了50%的代码量,消除了JDBC大量冗余代码

MyBatis通过提供简洁的API和自动化的数据库连接管理,减少了传统JDBC中大量的冗余代码。开发者无需手动管理数据库连接的打开和关闭,也无需编写繁琐的SQL语句拼接代码。

  1. 与数据库兼容性高

MyBatis与数据库的兼容性非常高,只要JDBC兼容的数据库,MyBatis都支持。这意味着开发者可以在不同的数据库之间轻松切换,而无需修改大量的代码。

  1. 提供映射标签,支持对象与数据库的ORM字段关系映射

MyBatis提供了丰富的映射标签,支持对象与数据库的ORM(Object-Relational Mapping)字段关系映射。开发者可以通过简单的配置,将数据库表中的字段映射到Java对象的属性上,从而简化了数据操作的复杂性。

JDBC连接数据库步骤

Java JDBC连接数据库的一般步骤如下:

1. 加载数据库驱动

在连接数据库之前,需要加载相应的数据库驱动。不同的数据库有不同的驱动类名。该步骤在Spring Boot中通常通过配置文件自动完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class JdbcExample {
public static void main(String[] args) {
String driver = "com.mysql.cj.jdbc.Driver";
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

2. 建立数据库连接

使用驱动类中的getConnection(url, userName, password)方法来连接数据库。

3. 创建Statement实例对象

通过上一步获得的数据库连接对象,调用createStatement方法创建一个Statement对象,用于执行SQL语句。

4. 执行SQL语句

使用Statement对象的executeQueryexecuteUpdate方法执行SQL语句。

5. 取出SQL运行结果

执行SQL语句后,可以通过ResultSet对象获取查询结果。

6. 关闭连接

在执行完数据库操作后,要关闭数据库连接以避免占用资源。关闭连接需要逐级进行,即先关闭ResultSet,再关闭Statement,最后是Connection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JdbcExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "root";
String password = "root";

String sql = "SELECT * FROM users";

Connection conn = null;
Statement stmt = null;
ResultSet rs = null;

try {
conn = DriverManager.getConnection(url, user, password);
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);

while (rs.next()) {
System.out.println(rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

原生MyBatis的应用

在Spring Boot项目中使用原生的MyBatis进行数据库操作,需要按照以下步骤进行配置和开发。以下是详细的步骤说明:

1. 配置MyBatis

  • 引入MyBatis依赖

在项目的pom.xml中引入MyBatis的依赖。

1
2
3
4
5
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
  • 配置数据源

在项目的application.ymlapplication.properties中配置数据源。

1
2
3
4
5
6
7
8
9
10
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.model
  • 创建SQL映射文件

在项目结构中创建SQL映射文件Mapper。xml,通常放在src/main/resources/mapper目录下。

2. 创建实体

创建用于映射数据库表的实体类,字段名与类型需要与数据库中对应表保持一致。

1
2
3
4
5
6
7
8
9
package com.example.model;

public class User {
private Long id;
private String name;
private String email;

// Getters and Setters
}

3. 编写SQL映射文件

创建XML文件,定义SQL语句和映射关系。

1
2
3
4
5
6
<!-- src/main/resources/mapper/UserMapper.xml -->
<mapper namespace="com.example.dao.UserDao">
<select id="getUserById" parameterType="long" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
</mapper>

4. 编写DAO接口

创建DAO接口,定义数据库操作方法。

1
2
3
4
5
6
7
package com.example.dao;

import com.example.model.User;

public interface UserDao {
User getUserById(Long id);
}

5. 编写具体的SQL语句

在SQL映射文件中实现DAO接口,编写具体的SQL语句。

1
2
3
4
5
6
<!-- src/main/resources/mapper/UserMapper.xml -->
<mapper namespace="com.example.dao.UserDao">
<select id="getUserById" parameterType="long" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
</mapper>

6. 调用DAO操作方法

在服务层或控制层调用DAO中的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.example.service;

import com.example.dao.UserDao;
import com.example.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

@Autowired
private UserDao userDao;

public User getUserById(Long id) {
return userDao.getUserById(id);
}
}

MyBatis中#$区别

在MyBatis中,#{}${}是两种不同的参数占位符,它们在处理SQL语句时有着不同的行为和用途。以下是对这两种占位符的详细解释及其区别:

#{} 占位符

MyBatis在处理#{}时,会创建预编译的SQL语句,将#{}替换为?,在具体执行SQL时会为预编译中的?赋值,调用PreparedStatementset方法来赋值。

  • 预编译SQL:预编译的SQL执行效率高,因为数据库可以缓存预编译的SQL语句,减少了解析和编译的开销。
  • 防止SQL注入:预编译的SQL语句可以防止SQL注入攻击,因为参数值会被正确地转义和处理。
  • 安全性高:适合传递参数,特别是用户输入的参数。
1
2
3
<select id="getUserById" parameterType="long" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>

${} 占位符

MyBatis在处理${}时,只会创建普通的SQL语句,然后在执行SQL语句时将参数拼接到SQL中。

优点:

  • 灵活性高:适合用于动态SQL拼接,如动态表名、列名等。

缺点:

  • 不能防止SQL注入:因为参数直接拼接到SQL语句中,如果参数未经过校验、过滤,可能会导致安全问题。
  • 执行效率低:每次执行SQL语句时都需要重新解析和编译SQL,效率较低。
1
2
3
<select id="getUserByDynamicColumn" parameterType="map" resultType="User">
SELECT * FROM users WHERE ${column} = #{value}
</select>

区别总结

特性 #{} 占位符 ${} 占位符
处理方式 预编译SQL,替换为?,调用set方法 直接拼接参数到SQL语句中
执行效率 高(预编译SQL) 低(每次都需要解析和编译SQL)
安全性 高(防止SQL注入) 低(不能防止SQL注入)
适用场景 传递参数(特别是用户输入的参数) 动态SQL拼接(如动态表名、列名等)
  • **优先使用#{}**:在大多数情况下,优先使用#{}占位符,因为它提供了更高的安全性和执行效率。
  • **谨慎使用${}**:仅在需要动态拼接SQL语句时使用${},并确保对参数进行严格的校验和过滤,以防止SQL注入攻击。

MyBatis与MyBatis Plus区别

MyBatis Plus是一个基于MyBatis的增强工具库,旨在简化开发并提高开发效率。以下是MyBatis Plus相较于MyBatis的主要区别和增强功能:

  1. CURD操作

MyBatis Plus通过集成BaseMapper接口,提供了一系列内置的快捷方法,使得CURD操作更加简单,无需编写重复的SQL语句。

1
2
3
public interface UserMapper extends BaseMapper<User> {
// 无需编写CURD方法,BaseMapper已经提供了
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<mapper namespace="com.example.dao.UserDao">
<select id="getUserById" parameterType="long" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
<insert id="insertUser" parameterType="User">
INSERT INTO users (name, email) VALUES (#{name}, #{email})
</insert>
<update id="updateUser" parameterType="User">
UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}
</update>
<delete id="deleteUser" parameterType="long">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>
  1. 代码生成器

MyBatis Plus提供了代码生成器功能,可以根据数据库表结构自动生成实体类、mapper接口以及XML映射文件,减少了手动编写的工作量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class CodeGenerator {
public static void main(String[] args) {
AutoGenerator mpg = new AutoGenerator();
// 配置数据源
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mydb");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
mpg.setDataSource(dsc);

// 配置生成策略
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
mpg.setStrategy(strategy);

// 执行生成
mpg.execute();
}
}
  1. 通用方法封装

MyBatis Plus封装了许多常用的方法,如条件构造器、排序、分页查询等,简化了开发过程。

1
2
3
4
5
public List<User> getUsersByCondition(String name, Integer age) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", name).eq("age", age);
return userMapper.selectList(queryWrapper);
}
  1. 分页插件

MyBatis Plus内置了分页插件,可以轻松实现分页查询功能。

1
2
3
4
public IPage<User> getUsersByPage(int page, int size) {
Page<User> pageParam = new Page<>(page, size);
return userMapper.selectPage(pageParam, null);
}
  1. 多租户支持

MyBatis Plus:MyBatis Plus提供了多租户支持,可以轻松实现多租户数据隔离的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
@Override
public Expression getTenantId() {
return new LongValue(1L); // 租户ID
}

@Override
public String getTenantIdColumn() {
return "tenant_id"; // 租户ID列名
}
}));
return interceptor;
}
}
  1. 注解支持

MyBatis Plus提供了丰富的注解支持,如@TableName@TableId@TableField等,简化了实体类的配置。

1
2
3
4
5
6
7
8
9
@TableName("users")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private String email;

// Getters and Setters
}