一. 概念
JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的 SQL 数据库存取和操作的公共接口
目标是使 Java 程序员使用 JDBC 可以连接任何提供了 JDBC 驱动程序的数据库系统
二. 通过 JDBC 连接数据库
1. 通常的连接方式
/**
* Driver 是一个接口: 数据库厂商必须提供实现的接口. 能从其中获取数据库连接.
* 可以通过 Driver 的实现类对象获取数据库连接.
*
* 1. 加入 mysql 驱动
* 1). 解压 mysql-connector-java-5.1.7.zip
* 2). 在当前项目下新建 lib 目录
* 3). 把 mysql-connector-java-5.1.7-bin.jar 复制到 lib 目录下
* 4). 右键 build-path , add to buildpath 加入到类路径下
*
*/
@Test
public void test() throws SQLException{
//1. 创建一个 Driver 实现类的对象
Driver driver = new com.mysql.cj.jdbc.Driver();
//2. 准备连接数据库的基本信息: url, user, password
String url="jdbc:mysql://localhost:3306/test";
Properties info = new Properties();
info.put("user", "root");
info.put("password", "");
//JDBC 5.1.28之前不会存在时区的问题,之后需要设置时区,不然会出差
info.put("serverTimezone", "GMT+8");
//3. 调用 Driver 接口的 connect(url, info) 获取数据库连接
Connection connect = driver.connect(url, info);
System.out.println(connect);
}
/**
* 编写一个通用的方法, 在不修改源程序的情况下, 可以获取任何数据库的连接
* 解决方案: 把数据库驱动 Driver 实现类的全类名、url、user、password 放入一个
* 配置文件中, 通过修改配置文件的方式实现和具体的数据库解耦.
* @throws Exception
*/
public Connection getConnection() throws Exception{
String driverClass=null;
String url=null;
String user=null;
String password=null;
//读取类路径下的 jdbc.properties 文件
InputStream is = getClass().getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(is);
driverClass=properties.getProperty("driver");
url=properties.getProperty("url");
user=properties.getProperty("user");
password=properties.getProperty("password");
//通过反射常见 Driver 对象.
Driver driver=(Driver) Class.forName(driverClass).newInstance();
Properties info = new Properties();
info.put("user", user);
info.put("password", password);
//通过 Driver 的 connect 方法获取数据库连接.
Connection connect = driver.connect(url, info);
return connect;
}
@Test
public void test2() throws Exception{
Connection connection = getConnection();
System.out.println(connection);
}
----------------------------------------------------------------------------------------
jdbc.properties 中的数据
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
user=root
password=
2.DriverManager 方式
/**
* DriverManager 是驱动的管理类.
* 1). 可以通过重载的 getConnection() 方法获取数据库连接. 较为方便
* 2). 可以同时管理多个驱动程序: 若注册了多个数据库连接, 则调用 getConnection()
* 方法时传入的参数不同, 即返回不同的数据库连接。
*
*/
public Connection getConnection2() throws Exception{
//1. 准备连接数据库的 4 个字符串.
//1). 创建 Properties 对象
Properties properties = new Properties();
//2). 获取 jdbc.properties 对应的输入流
InputStream in =
this.getClass().getClassLoader().getResourceAsStream("jdbc.properties");
//3). 加载 2) 对应的输入流
properties.load(in);
//4). 具体决定 user, password 等4 个字符串.
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String jdbcUrl = properties.getProperty("url");
String driver = properties.getProperty("driver");
//2. 加载数据库驱动程序(对应的 Driver 实现类中有注册驱动的静态代码块.)
Class.forName(driver);
//3. 通过 DriverManager 的 getConnection() 方法获取数据库连接.
return DriverManager.getConnection(jdbcUrl, user, password);
}
三. 通过 Statement 执行添加修改删除操作
/**
* 通过 JDBC 向指定的数据表中插入一条记录.
*
* 1. Statement: 用于执行 SQL 语句的对象
* 1). 通过 Connection 的 createStatement() 方法来获取
* 2). 通过 executeUpdate(sql) 可以执行 SQL 语句.
* 3). 传入的 SQL 可以是 INSRET, UPDATE 或 DELETE. 但不能是 SELECT
*
* 2. Connection、Statement 都是应用程序和数据库服务器的连接资源. 使用后一定要关闭.
* 需要在 finally 中关闭 Connection 和 Statement 对象.
*
* 3. 关闭的顺序是: 先关闭后获取的. 即先关闭 Statement 后关闭 Connection
*/
@Test
public void testStatement() throws Exception{
//1. 获取数据库连接
Connection conn = null;
Statement statement = null;
try {
conn = getConnection2();
//3. 准备插入的 SQL 语句
String sql = null;
// sql = "INSERT INTO customers (NAME, EMAIL, BIRTH) " +
// "VALUES('XYZ', '[email protected]', '1990-12-12')";
// sql = "DELETE FROM customers WHERE id = 1";
sql = "UPDATE customers SET name = 'TOM' " +
"WHERE id = 4";
System.out.println(sql);
//4. 执行插入.
//1). 获取操作 SQL 语句的 Statement 对象:
//调用 Connection 的 createStatement() 方法来获取
statement = conn.createStatement();
//2). 调用 Statement 对象的 executeUpdate(sql) 执行 SQL 语句进行插入
statement.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
//5. 关闭 Statement 对象.
if(statement != null)
statement.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
//2. 关闭连接
if(conn != null)
conn.close();
}
}
}
四. 通过 ResultSet 执行查询操作
/**
* ResultSet: 结果集. 封装了使用 JDBC 进行查询的结果.
* 1. 调用 Statement 对象的 executeQuery(sql) 可以得到结果集.
* 2. ResultSet 返回的实际上就是一张数据表. 有一个指针指向数据表的第一样的前面.
* 可以调用 next() 方法检测下一行是否有效. 若有效该方法返回 true, 且指针下移. 相当于
* Iterator 对象的 hasNext() 和 next() 方法的结合体
* 3. 当指针对位到一行时, 可以通过调用 getXxx(index) 或 getXxx(columnName)
* 获取每一列的值. 例如: getInt(1), getString("name")
* 4. ResultSet 当然也需要进行关闭.
*/
@Test
public void testResultSet(){
//获取 id=4 的 customers 数据表的记录, 并打印
Connection conn = null;
Statement statement = null;
ResultSet rs = null;
try {
//1. 获取 Connection
conn = JDBCTools.getConnection();
System.out.println(conn);
//2. 获取 Statement
statement = conn.createStatement();
System.out.println(statement);
//3. 准备 SQL
String sql = "SELECT id, name, email, birth " +
"FROM customers";
//4. 执行查询, 得到 ResultSet
rs = statement.executeQuery(sql);
System.out.println(rs);
//5. 处理 ResultSet
while(rs.next()){
int id = rs.getInt(1);
String name = rs.getString("name");
String email = rs.getString(3);
Date birth = rs.getDate(4);
System.out.println(id);
System.out.println(name);
System.out.println(email);
System.out.println(birth);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
//6. 关闭数据库资源.
JDBCTools.release(rs, statement, conn);
}
}
@Test
public void testSelect() throws Exception{
Connection connection = getConnection2();
String sql="select * from customers where 1 =1 ";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
resultSet.last();
if (resultSet.getRow()==0) {
System.out.println("没有查到数据");
}
else{
resultSet.beforeFirst();
while (resultSet.next()) {
System.out.println(resultSet.getString("name"));
}
}
connection.close();
}
五. PreparedStatement 类
1. 概念
是 Statement 的子接口,可以传入带占位符的 SQL 语句,并且提供了补充占位符变量的方法
可以有效的禁止 SQL 注入
能最大可能提高性能
索引下标是从1开始的
String sql = "INSERT INTO customers (name, email, birth) VALUES(?,?,?)";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "ATGUIGU");
preparedStatement.setString(2, "[email protected]");
preparedStatement.setDate(3,new Date(new java.util.Date().getTime()));
preparedStatement.executeUpdate();// 此时不需要再传入 SQL 语句
六. 通用的查询方法
/**
* 通用的查询方法:可以根据传入的 SQL、Class 对象返回 SQL 对应的记录的对象
* @param clazz: 描述对象的类型
* @param sql: SQL 语句。可能带占位符
* @param args: 填充占位符的可变参数。
* @return
*/
public <T> T get(Class<T> clazz, String sql, Object... args) {
T entity = null;
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//1. 得到 ResultSet 对象
connection = JDBCTools.getConnection();
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
resultSet = preparedStatement.executeQuery();
//2. 得到 ResultSetMetaData 对象
ResultSetMetaData rsmd = resultSet.getMetaData();
//3. 创建一个 Map<String, Object> 对象, 键: SQL 查询的列的别名,
//值: 列的值
Map<String, Object> values = new HashMap<>();
//4. 处理结果集. 利用 ResultSetMetaData 填充 3 对应的 Map 对象
if(resultSet.next()){
for(int i = 0; i < rsmd.getColumnCount(); i++){
String columnLabel = rsmd.getColumnLabel(i + 1);
Object columnValue = resultSet.getObject(i + 1);
values.put(columnLabel, columnValue);
}
}
//5. 若 Map 不为空集, 利用反射创建 clazz 对应的对象
if(values.size() > 0){
entity = clazz.newInstance();
//5. 遍历 Map 对象, 利用反射为 Class 对象的对应的属性赋值.
for(Map.Entry<String, Object> entry: values.entrySet()){
String fieldName = entry.getKey();
Object value = entry.getValue();
ReflectionUtils.setFieldValue(entity, fieldName, value);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCTools.releaseDB(resultSet, preparedStatement, connection);
}
return entity;
}
七. DAO 设计模式
1. 访问数据信息的类。包含了对数据的CRUD,而不包含任何业务相关的信息
2. 使用 beanutils 工具类操作
①. 导入 commons-logging-1.1.1.jar 和 commons-beanutils-1.8.0.jar
②. 使用 beanutils.setProperty 和 beanutils.getProperty
八. 处理元数据
1. DatabaseMetaData 是描述数据库的元数据对象
@Test
public void test() {
Connection connection=null;
DatabaseMetaData data=null;
ResultSet resultSet=null;
try {
connection=JDBCTools.getConnection();
data=connection.getMetaData();
// 获取数据库版本号
System.out.println(data.getDatabaseMajorVersion());
// 获取连接到数据库的用户名
System.out.println(data.getUserName());
// 得到有哪些数据库
resultSet=data.getCatalogs();
while (resultSet.next()) {
System.out.println(resultSet.getString(1));
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
JDBCTools.releaseDB(resultSet, null, connection);
}
}
2. ResultSetMetaData 类
/**
* ResultSetMetaData: 描述结果集的元数据.
* 可以得到结果集中的基本信息: 结果集中有哪些列, 列名, 列的别名等.
* 结合反射可以写出通用的查询方法.
*/
resultSet = preparedStatement.executeQuery();
Map<String, Object> values = new HashMap<String, Object>();
ResultSetMetaData rsmd = resultSet.getMetaData();
while(resultSet.next()){
//2. 打印每一列的列名
for(int i = 0; i < rsmd.getColumnCount(); i++){
// 获取列的别名
String columnLabel = rsmd.getColumnLabel(i + 1);
// 获取列的值
Object columnValue = resultSet.getObject(columnLabel);
values.put(columnLabel, columnValue);
}
}
九. 取得数据库自动生成的主键
/**
* 取得数据库自动生成的主键
*/
@Test
public void testGetKeyValue() {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JDBCTools.getConnection();
String sql = "INSERT INTO customers(name, email, birth)" +
"VALUES(?,?,?)";
// preparedStatement = connection.prepareStatement(sql);
//使用重载的 prepareStatement(sql, flag)
//来生成 PreparedStatement 对象
preparedStatement = connection.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS);
preparedStatement.setString(1, "ABCDE");
preparedStatement.setString(2, "[email protected]");
preparedStatement.setDate(3,
new Date(new java.util.Date().getTime()));
preparedStatement.executeUpdate();
//通过 getGeneratedKeys() 获取包含了新生成的主键的 ResultSet 对象
//在 ResultSet 中只有一列 GENERATED_KEY, 用于存放新生成的主键值.
ResultSet rs = preparedStatement.getGeneratedKeys();
if(rs.next()){
System.out.println(rs.getObject(1));
}
ResultSetMetaData rsmd = rs.getMetaData();
for(int i = 0; i < rsmd.getColumnCount(); i++){
System.out.println(rsmd.getColumnName(i + 1));
}
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCTools.releaseDB(null, preparedStatement, connection);
}
}