MyBatis概览

2024/1/5 MyBatis框架

# 1、架构分层

Java在开发Web应用时,一般将系统应用分为三层:表现层、业务逻辑层和数据访问层。(可以理解为类似MVC一样分层)。

  • 三层架构

  • 表现层(Controller层):处理用户界面和交互,展示数据和接收用户输入。
  • 业务逻辑层(Service层):包含核心业务逻辑,处理和管理应用程序的业务规则和流程。
  • 数据访问层(Dao层):处理与数据的交互和持久化,与数据库或其他数据存储系统进行交互。

理解一下三层架构

  • 表现层(Controller层)负责接收用户请求,并调用业务逻辑层来处理请求,最后将处理结果返回给用户。
  • 业务逻辑层(Service层)负责处理业务逻辑,主要对业务规则和流程进行控制。
  • 数据数据访问层(Dao层)负责与数据库进行交互,包括数据的增删改查等操作。
  • 三层架构的优点

  • 分层设计:三层架构通过将系统划分为不同的层次,实现了系统的分层设计。这种分层设计使得系统结构更加清晰,各层职责明确,便于维护和扩展。
  • 降低耦合度:三层架构降低了各层之间的耦合度,使得各层之间的依赖关系更加简单。这有助于提高系统的可维护性和可重用性。
  • 提高可扩展性:三层架构的设计使得系统易于扩展。
  • 提高安全性:三层架构职责分明,每层做好数据安全,从而提高了系统整体的安全性。
  • 便于测试和调试:三层架构使得测试和调试更加方便。可以针对不同的层次进行单元测试、集成测试等,便于定位和解决问题。
  • 提高开发效率:三层架构使得开发人员可以分工合作,并行开发。

# 2、数据库操作方式

# 2.1、JDBC方式

步骤

  • 加载JDBC驱动程序
  • 建立数据库连接
  • 创建PreparedStatement对象
  • 执行SQL语句
  • 遍历处理结果
  • 关闭所有JDBC资源连接

JDBC案例源码

创建users表
CREATE TABLE users (  
    SNo VARCHAR(255),  
    SName VARCHAR(255),  
    Birth VARCHAR(255),
    SPNo VARCHAR(255),  
    Major VARCHAR(255),  
    Grade VARCHAR(255),  
    SInstructor VARCHAR(255),  
    SPwd VARCHAR(255)  
);
1
2
3
4
5
6
7
8
9
10
public class JDBCExample {  
    public static void main(String[] args) {  
        String url = "jdbc:mysql://localhost:3306/mydatabase"; // 数据库连接URL  
        String username = "root"; // 数据库用户名  
        String password = "password"; // 数据库密码  
  
        Connection connection;
        PreparedStatement statement;
        ResultSet resultSet;
        try {  
            // 加载JDBC驱动程序  
            Class.forName("com.mysql.cj.jdbc.Driver");  
  
            // 建立数据库连接  
            connection = DriverManager.getConnection(url, username, password);  
  
            // 在此处执行数据库操作,例如查询、插入、更新和删除等  
            /** 预编译SQL */
            statement = connection.prepareStatement("select * from  users");
            /** 执行查询 */
            resultSet = statement.executeQuery();
            /** 读取结果 */
            while(resultSet.next()){
                System.out.println("SNo="+resultSet.getString("SNo"));
                System.out.println("SName="+resultSet.getString("SName"));
                System.out.println("Birth="+resultSet.getString("Birth"));
                System.out.println("SPNo="+resultSet.getString("SPNo"));
                System.out.println("Major="+resultSet.getString("Major"));
                System.out.println("Grade="+resultSet.getString("Grade"));
                System.out.println("SInstructor="+resultSet.getString("SInstructor"));
                System.out.println("SPwd="+resultSet.getString("SPwd"));
            }
        } catch (ClassNotFoundException e) {  
            e.printStackTrace();  
        } catch (SQLException e) {  
            e.printStackTrace();  
        } finally {
            // 关闭数据库连接  
            connection.close();  
            statement.close();
            resultSet.close();
        }
    }  
}
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
40
41
42
43
44

JDBC案例中,我们简单可以这样测试连接数据库,但如果在项目中使用,我们就需要进行封装,因为对于数据库的连接、查询、关闭等操作上来说,都是重复性操作。

JDBC通用封装

数据库信息枚举,或者你也可以放到配置文件中读取

public enum JdbcJdbcConstants {  
    INSTANCE;  
  
    public static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";  
    public static final String JDBC_URL = "jdbc:mysql://localhost:3306/mydatabase";  
    public static final String JDBC_USER = "root";  
    public static final String JDBC_PASSWORD = "password";  
}
1
2
3
4
5
6
7
8

创建JDBC工具类

public class JdbcUtil {
	
	/**
	 * 类加载时加载数据库驱动
	 */
	static{
		try {
			Class.forName(JdbcConstants.JDBC_DRIVER);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			System.out.println("驱动加载失败!");
			e.printStackTrace();
		}
	}
	
	
	/**
	 * 获取数据库连接的方法
	 * @return 数据库连接对象conn
	 */
	private static Connection getConnection(){
		Connection conn = null;
		try {
			conn = DriverManager.getConnection(JdbcConstants.JDBC_URL, JdbcConstants.JDBC_USER, JdbcConstants.JDBC_PASSWORD);
		} catch (SQLException e) {
			System.out.println("获取连接失败!");
			e.printStackTrace();
		}
		return conn;
	}
	
	/**
	 * 执行更新操作(插入、修改、删除)
	 * @param sql 要执行的SQL语句
	 * @param params SQL语句预编译参数(如无可省略)
	 * @return rows 影响的行数
	 */
	public static int executeUpdate(String sql,Object...params){
		Connection conn = getConnection();
		PreparedStatement ps = null;
		int rows=0;
		try {
			ps = getConnection().prepareStatement(sql);
			if(params!=null&&params.length>0){
				for(int i=0;i<params.length;i++){
					ps.setObject(i+1,params[i]);
				}
			}
			rows = ps.executeUpdate();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				closeResource(conn, ps, null);
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return rows;
		
	}
	
	/**
	 * 执行查询操作
	 * @param sql 要执行的查询sql语句
	 * @param params SQL语句预编译参数(如无可省略)
	 * @return list 结果集,每一条结果为所有查询的字段名和字段值为键值对的Map集合
	 */
	public static List<Map<String, Object>> executeQuery(String sql,Object...params){
		Connection conn = getConnection();
		PreparedStatement ps=null;
		ResultSet set=null;
		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
		try {
			ps = conn.prepareStatement(sql);
			if(params!=null&&params.length>0){
				for(int i=0;i<params.length;i++){
					ps.setObject(i+1,params[i]);
				}
			}
			set = ps.executeQuery();
			ResultSetMetaData rsmd = set.getMetaData();
			int columnCount = rsmd.getColumnCount();
			while(set!=null&&set.next()){
				Map<String,Object> map = new HashMap<String,Object>();
				for(int i=1;i<=columnCount;i++){
					map.put(rsmd.getColumnName(i), set.getObject(i));
				}
				list.add(map);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			try {
				closeResource(conn,ps,set);
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return list;
	}
	
	/**
	 * 释放资源
	 * @param conn Connection对象
	 * @param ps PreparedStatement对象
	 * @param rs ResultSet对象
	 * @throws SQLException
	 */
	private static void closeResource(Connection conn,PreparedStatement ps,ResultSet rs) throws SQLException{
		if(rs!=null){
			rs.close();
		}
		if(ps!=null){
			ps.close();
		}
		if(conn!=null){
			conn.close();
		}
	}
}

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

测试查询

public class JDBCExample2 {  
    public static void main(String[] args) {  
        String sql = "select * from users";
        List<Map<String, Object>> list = JdbcUtil.executeQuery(sql,null);
        for(Map<String, Object> map:list){
            System.out.println("SNo="+map.get("SNo").toString());
            System.out.println("SName="+map.get("SName").toString());
            System.out.println("Birth="+map.get("Birth").toString());
            System.out.println("SPNo="+map.get("SPNo").toString());
            System.out.println("Major="+map.get("Major").toString());
            System.out.println("Grade="+map.get("Grade").toString());
            System.out.println("SInstructor="+map.get("SInstructor").toString());
            System.out.println("SPwd="+map.get("SPwd").toString());
        }
    }  
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 2.2、第三方框架方式

对于JDBC操作,经过我们简单的封装可以在项目中使用,但是对于Dao层涉及了很多其他的操作,数据安全校验、事务等操作,我们都是没有考虑的,但我们使用第三方框架可以简化开发。

  • 第三方框架的好处

  • 简化数据库操作:使用JDBC第三方框架可以简化数据库操作,使得开发人员能够更加高效地与数据库进行交互。这些框架通常提供了一组易于使用的API,使得开发人员可以更加方便地执行数据库查询、插入、更新和删除等操作。
  • 连接池管理:JDBC第三方框架通常提供了连接池管理功能,可以有效地管理数据库连接。通过连接池,开发人员可以重复使用现有的数据库连接,而不是为每个新的操作创建新的连接。这可以提高应用程序的性能和响应速度。
  • 事务管理:JDBC第三方框架通常也提供了事务管理功能。事务是确保数据库完整性的关键,这些框架可以帮助开发人员更好地管理事务,确保数据的完整性和一致性。
  • 数据库无关性:使用JDBC第三方框架可以使开发人员编写与数据库无关的代码。这意味着开发人员可以使用相同的代码来与不同的数据库进行交互,提高了代码的可重用性和可维护性。
  • 安全性:JDBC第三方框架通常也提供了安全性功能,可以帮助开发人员保护敏感数据。这些框架可以加密数据、验证用户身份、限制对数据库的访问等,从而确保数据的安全性。
  • 常用的第三方JDBC框架

  • SpringJDBC:提供JDBC的封装,简化数据库操作。
  • MyBatis:流行的ORM框架,通过XML或注解方式实现SQL与Java对象的映射。
  • Hibernate:成熟的ORM框架,提供对象关系映射和查询支持。
  • Apache Commons DbUtils:轻量级JDBC工具框架,提供简单的数据库操作API。

# 3、MyBatis框架

# 3.1、MyBatis概述

  • Mybatis是一个持久层框架。
  • 是一个流行的ORM框架,通过XML或注解的方式实现了SQL语句和Java对象的映射关系。
  • 使得开发者可以更轻松地编写和维护SQL语句,同时降低了SQL代码与Java代码之间的耦合度。
  • 支持动态SQL生成,可以根据传入的数据动态生成SQL语句,从而增加了灵活性。
  • 支持使用接口和注解来映射数据库表和Java对象之间的关系。
  • 提供了一个简单易用的API,可以轻松地操作数据库。

ORM解释

  • ORM是Object-Relational Mapping的缩写,中文可以称为对象关系映射。它是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。
  • ORM通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。它提供了一种实现持久化层的模式,使得ORM中间件能在任何一个应用的业务逻辑层和数据库层之间充当桥梁。
  • ORM的目的是解决面向对象与关系数据库存在的互不匹配的现象,使得在操作业务对象时,不需要再去和复杂的SQL语句打交道,只需简单的操作对象的属性和方法。
  • ORM模型通常提供了一些核心原则,如对象关系映射(Object-Relational Mapping)、封装、聚合、耦合等,使得开发者可以更方便地编写和维护SQL语句。它简化了数据库查询过程,提供了一种虚拟对象数据库的概念,使得对象关系映射成为可能。

# 3.2、MyBatis的优势

  • 灵活性:MyBatis允许开发者直接编写SQL语句,这使得开发者可以更加灵活地控制SQL查询,而不是被框架的查询语言所限制。
  • 性能:由于MyBatis允许直接编写SQL语句,因此它可以避免一些ORM框架的性能开销,特别是在处理复杂的数据库操作时。
  • 简洁:MyBatis的XML配置文件或注解方式使得它更加简洁和易于理解,尤其是在处理复杂的查询和更新操作时。
  • 可维护性:MyBatis的映射文件和接口分离的设计使得代码更加清晰,易于维护和修改。
  • 集成度高:MyBatis可以和许多其他的Java框架集成,如Spring,这样可以将MyBatis和其他功能一起使用,如事务管理、安全性等。

相比之下,SpringJDBC主要侧重于JDBC的封装和简化,而Hibernate和Apache Commons DbUtils则更侧重于提供更高级的ORM功能。MyBatis在灵活性、性能和简洁性方面具有优势,使得它在许多场景下成为更好的选择。

# 3.3、MyBatis主要功能

  • SQL映射:将SQL语句与Java方法进行映射。
  • 参数和结果映射:支持参数传递和查询结果的映射。
  • 缓存支持:提供会话级和应用程序级的缓存。
  • 动态SQL:根据条件生成不同的SQL语句。
  • 延迟加载:减少数据库访问次数,提高性能。
  • 插件支持:扩展MyBatis的功能。
  • 批量操作:提高数据库操作的效率。
  • 与数据库交互:分离SQL语句和Java代码,方便后期修改。
  • 定制化SQL、存储过程和高级映射:简化JDBC代码,提高开发效率。