java 数据库连接JDBC,从入门到精通,深入浅出,看这一篇就够了

Java 原生提供了java.sql.*包用于与数据库交互,然而只是接口而已,你还需要导入对应数据库的驱动库实现,譬如mysql-connector-java.jar,什么?你说你不知道?叫你教授微信发你。

导入 MySQL 数据库驱动

如果已有 mysql-connector-java.jar库,可以在 IED 里面导入,在此不再展开,自行搜索。
如果没有,如果你是 Maven 项目,在 pom.xml里面的 <dependencies>标签内插入:

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>版本</version>
</denpendency>

JDBC API 入门

java.sql.*有几个很重要的类,分别是:

作用
DriverManager驱动管理器,负责注册驱动和创建数据库连接
Connection数据库连接,负责开启数据库查询删除等操作
Statement 和 PreparedStatement负责操作 SQL 语句
ResultSet储存数据库SELECT语句查询的结果
SQLExceptionStatementPreparedStatement所有的方法都会强制抛出此异常,必须得用 try_catch 语句捕获

注册 MySQL 驱动类

DriverManager的常用方法:

// 手动注册驱动类
static void registerDriver(java.sql.Driver) throws SQLException

// 手动卸载驱动类
static void deregisterDriver(java.sql.Driver) throws SQLException

// 根据 URL, 数据库用户名及其密码,开启 Connection 类,此很重要,是我们开启查询删除插入操作的把柄
static Connection getConnection(String url, // URL 填 "jdbc:mysql://localhost:3306/数据库名称"
                                String username,
                                String password) throws SQLException

接下来是我们注册驱动类,
旧版:

Class.forName("com.mysql.jdbc.Driver"); // 用类加载器加载驱动类
DriverManager.registerDriver(com.mysql.jdbc.Driver.class); // 用驱动加载器挂载

新版:

Class.forName("com.mysql.jdbc.Driver"); // 自动挂载,只需类加载器加载就行了

然后再用DriverManager.getConnection方法获取Connection类,这个类是我们打开增删改查的把柄
总之:

protected static final Connection CONN;
// 全局静态代码块,只加载一次,以免多次加载带来额外的I/O开销
static {
	try {
		Class.forName("com.mysql.jdbc.Driver"); // 注册 MySQL 驱动
		// 在实际开发中不要犯密码写进代码的低级错误,这里只是举例子
		CONN = DriverManager.getConnection
				("jdbc:mysql://localhost:8080/数据库名字", "用户名", "密码");
	} catch (ClasNotFoundException | SQLException e) {
		throw new ExceptionInInitializerError(e);
	}
}

小提示:不建议将密码写进源代码,一旦源代码泄漏,安全风险很大。正确的做法是将用户名和密码分开写成一个配置文件,然后设计一个专门的类来读取。感兴趣可以了解 c3p0连接池库,它自带了一个配置文件系统

配置文件

写配置文件jdbc.properties

# 连接数据库 URL
url=jdbc:mysql://localhost:3306/数据库名称

# 数据库驱动类全类名
driver=com.mysql.jdbc.Driver
username=root
password=toor

读取配置文件的类

import java.io.IOException;
import java.util.Properties;

public class PropertiesLoader {
	/**
	 * 配置文件主体
	 */
	private static final Properties props;
	
	/**
	 * 默认的配置文件名
	 */
	private static final String DEFAULT_NAME = "jdbc.properties";
	static {
		try {
			props.load(getClassLoader().getResourceAsStream(DEFAULT_NAME));
		} catch (IOException e) {
			throw new ExceptionInInitializerError(e);
		}
	}
	
	public static Properties getProperties() { return props; }
}
protected static final Connection CONN;
static {
	try {
		final Properties cfg = PropertiesLoader.getProperties();
		
		final String driver   = cfg.get("driver");
		final String url      = cfg.get("url");
		final String username = cfg.get("username");
		final String password = cfg.get("password");
		
		Class.forName(driver);
		CONN = DriverManager.getConnection(url, username, password);
	} catch (ClassNotFoundException | SQLException e) {
		throw new ExceptionInInitializerError(e);
	}
}

小提示:给业务上永远不需要改变的变量加上final关键字,有助于编译器优化。给方法加上final关键字可以让 JVM 虚拟机内联优化。官方用专门的注解类@ForcedInline来强制给方法内联


写用户类

这里我用了lombok库的注解来自动生成 getter 和 setter,毕竟手动编写简直浪费时间,懒惰是程序员第一美德

import java.util.Objects;
import lombok.*;

@Getter
@Setter
public class User {
	public static final int ADMIN_ROLE = 0;
	public static final int USER_ROLE = 1;
	public static final String DEFAULT_NAME = "匿名";

	private long id;
	private String name;
	private String password;
	private int role;
	
	public User(long id, String name, String password, int role) {
		this.id = id;
		this.name = Objects.requireNonNullElse(name, DEFAULT_NAME);
		this.password = Objects.requireNonNull(password);
		this.role = role;
	}

	public User(long id, String name, String password) {
		this(id, name, password, USER_ROLE);
	}
	
	public User(String name, String password) {
		this(-1, name, password);
	}
}

数据库增删查改操作

Connection类常用方法

// 创建语句操作
Statement createStatement();

// 根据SQL语句新建句柄
PreparedStatement prepareStatement(String sqlString);
void close();

// 回滚事务
void rollback()

PreparedStatement类常用方法

// 设置参数
void setByte(int paramIndex,   byte x);
void setShort(int paramIndex,  short x);
void setInt(int paramIndex,    int x);
void setLong(int paramIndex,   long x);
void setString(int paramIndex, String x);

// 执行查询操作,返回结果集合
ResultSet executeQuery() throws SQLException; 
 // 执行删除或插入操作
boolean execute() throws SQLException;

void close() // 关闭,一定要使用这个方法!
void closeOnCompletion();

以下是PreparedStatement查找的例子

public User findUserById(long id) throws SQLException {
	try (var pstmt = CONN.prepareStatement("SELECT * FROM users WHERE id=?")) {
		pstmt.setLong(1, id);
		final ResultSet r = pstmt.executeQuery();
		return r.next() ? new User(id, rs.getString("username"), rs.getString("password")) : null;
	}
	return null;
}

删除

public void removeUser(long id) throws SQLException {
	try (var pstmt = CONN.prepareStatement("DELETE FROM user WHERE id=?")) {
		pstmt.setLong(1, id);
		pstmt.execute();
	}
}

设计 DAO 层

首先,我们要明确 DAO (Database Access Operation) 的设计模式,模块化编程,代码写成一坨只会增加开发 & 维护难度,毕竟论起写💩山代码没人比我更在行。

[接口] UserDatabaseAccessor (定义了一系列方法)
                |
                +---- [类] MySQLDatabaseAccessor  (MySQL 数据库实现)
                |
                +---- [类] OracleDatabaseAccessor (Oracle 数据库实现)
                |
                +---- [类] RedisDatabaseAccessor  (Radis 数据库实现)

定义 DAO 接口

public interface UserDataBaseAccessor {
	public void add(User user); // 添加用户
	
	public void remove(long id); // 根据用户id移除用户
	
	public default void remove(User user) {
		remove(user.getId());
	}
	
	public List<User> queryAll(); // 返回所有用户的列表

	public List<User> queryAll(String name);
	
	public User find(long id); // 通过id查找指定用户
}

写 MySQL 数据库 DAO 实现

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class MySQLDatabaseAccessor implements UserDatabaseAccessor {
	/**
	 * 静态加载,就加载一次,重复加载浪费I/O
	 */
    protected static final Connection CONN;
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver"); // 注册 MySQL 驱动
            // 在实际开发中不要犯密码写进代码的低级错误,这里只是举例子
            CONN = DriverManager.getConnection
                    ("jdbc:mysql://localhost:8080/数据库名字", "用户名", "密码");
        } catch (ClasNotFoundException | SQLException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

	@Override
	public List<User> queryAll() throws SQLException {
		return newStatement("SELECT * FROM users").getResultList();
	}

	@Override
	public List<User> queryAll(String name) throws SQLException {
		return newStatement("SELECT * FROM users").getResultStream()
		       .filter(user -> user.getName().equals(name))
		       .toList();
	}

	@Override
	public User find(long id) throws SQLException {
		return newStatement("SELECT * FROM users WHERE id=?")
		       .setParameter(1, id)
		       .getResultList().get(0);
	}
	public void remove(long id) throws SQLException {
		newStatement("DELETE FROM user WHERE id=?")
				.setParameter(1, if)
				.execute();
	}
	
	@Override
	public void add(User user) {
		newStatement("INSERT INTO users(username, password, role) VALUES(?, ?, ?)")
				.setParameter(1, user.getName())
				.setParameter(2, user.getPassword())
				.setParameter(3, user.getRole())
				.execute();
	}

	protected Query newStatement(String sqlString) throws SQLException {
		return new Query(CONN.prepareStatement(sqlString));
	}
	
	// 链式构造工具类
	static final class Query {
		private final PreparedStatement pstmt;
		Query(PreparedStatement pstmt) { this.pstmt = pstmt; }
		
		public void execute() throws SQLException {
			pstmt.execute();
		}
		public List<User> getResultList() throws SQLException {
			final ResultSet rs = pstmt.executeQuery();
			List<User> result = new ArrayList<>();
			while (rs.next()) {
				long id.        = rs.getLong("id");
				String name     = rs.getString("username");
				String password = rs.getString("password");
				int role.       = rs.getInt("role");
				result.add(new User(id, name, password, role));
			}
			return result.isEmpty() ? null : result;
		}
		public Stream<User> getResultStream() throws SQLException {
			List<User> l;
			return (l = getResultList()) == null ? null : l;
		}
		public Query setParameter(int parameterIndex, long x) throws SQLException {
			pstmt.setInt(parameterIndex, x);
			return this;
		}
		public Query setParameter(int parameterIndex, String x) throws SQLException {
			pstmt.setString(parameterIndex, x);
			return this;
		}
		// TODO: ...
	}
}
public final class Accessors {
	private Accessors() { return new AssertionError(); }

	public static UserDatabaseAccessor newMySQLPool() {
		return new MySQLUserDatabaseAccessor();
	}
	public static UserDatabaseAccessor newOraclePool() {
		return new OracleUserDatabaseAccessor();
	}
}
  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值