JDBC(MySQL)一周学习总结(一)

时间:2023-03-09 14:55:16
JDBC(MySQL)一周学习总结(一)
一周过去了,我在这分享一下这一周来学习 JDBC 的知识,同时也希望可以帮到别人!

首先我们从获取 JDBC 连接开始

Driver(每个驱动程序类必须实现的接口)

获取数据库连接需要配置数据库连接信息,DriverClass 表示数据库驱动,user 表示数据库登录用户名,passWord 表示登录密码,url 用于标识一个被注册的驱动程序,驱动程序管理器通过 URL 选择正确的驱动程序,从而建立数据库连接

Oracle URL:jdbc:oracle:thin:@localhost:1521:数据库名

SQLServer URL:jdbc:microsoft:sqlserver//localhost:1433;DatabaseName=数据库名

MySQL URL:jdbc:mysql://localhsot:3306/数据库名;如果你的mysql 数据库默认端口没有改变其 URL 可以简写为 jdbc:mysql:///数据库名

下面就是获取数据库连接的代码:

package com.java.jdbc.test;

import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Properties; import org.junit.Test; public class TestConnection { // DriverClass 利用不通的构造器去创建一个对象
@Test
public void testDriver() throws SQLException {
// 连接数据 Mysql
Driver driver = new com.mysql.jdbc.Driver();
// 准备数据库连接信息
String url = "jdbc:mysql://localhost:3306/sh_db"; Properties info = new Properties();
// 我们除了可以利用 put 方法将连接信息存入 properties 对象,还可以利用 setProperty 去设置属性
// 用户名
info.put("user", "root");
// 密码
info.put("password", "zy961029"); Connection connection = driver.connect(url, info);
// 打印数据库连接信息
System.out.println(connection);
}
}

上面的代码是最基本的连接数据库的实现,但是我们要使用上面的代码去实现连接不同的数据库的时我们就需要去改变源代码中的数据库信息,这样做肯定是不方便,且容易出错的,所以我们接下来实现利用外部配置文件的去实现获取数据库连接

package com.java.jdbc.test;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.Driver;
import java.util.Properties; import org.junit.Test; public class TestConnection { // 此方法具有普遍性,在本方法中没有和任何数据库厂商相连接,只需要改变配置文件即可达到连不同的数据库
public Connection getConnection() throws Exception {
String driverClass = null;
String url = null;
String user = null;
String password = null; InputStream in = getClass().getClassLoader().getResourceAsStream("jdbc.properties"); // 通过反射获得配置文件,用 properties 类来获取配置文件的属性
Properties properties = new Properties();
properties.load(in);
driverClass = properties.getProperty("driverClass");
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password"); Driver driver = (Driver) Class.forName(driverClass).newInstance(); Properties info = new Properties();
info.put("user", user);
info.put("password", password); Connection connection = driver.connect(url, info); return connection;
}
} jdbc.properties
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/sh_db
user=root
password=zy961029

DriverManager

管理一组 JDBC 驱动程序的基本服务,可以通过重载的 getConnection() 获取连接更加方便,可以同时管理多个驱动程序,若注册了多个数据库驱动,只需要给 getConnection 方法传入不同的参数即可,下面是利用 DriverManager 获取数据库连接

@Test
public void getConnection() {
// 通过反射获取配置文件
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
try {
// 获取属性属性值
properties.load(inputStream);
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driverClass"); // 加载数据库驱动类(注册驱动)
// 注册驱动本应如下注册,但在 com.mysql.Driver 中的静态代码块已经将其注册了,所以不需在写一遍
// DriverManager.registerDriver(Class.forName(driver).newInstance());
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
System.out.printf(String.valueOf(connection));
} catch (IOException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} }

通过上面的介绍我们现在获取到了数据库的连接,那么接下来就是操作数据库(增删改查,首先利用 Statement,使用完毕需要释放)

@Test
public void testInser() {
// 获取数据库连接
Connection connection = getConnectionMy();
// statement 对象是操作 sql 语句的对象
Statement statement = null;
try {
// 获取 statement 对象
statement = connection.createStatement(); String sql = null;
// sql = "INSERT INTO book (BOOK_NAME, ISBN, PRICE, STOCK) VALUES ('SQLServer', '1004', '150', '30' )";
// sql = "DELETE FROM book WHERE id=4";
sql = "UPDATE book SET PRICE=400 WHERE id=5";
// 执行 sql 语句
statement.execute(sql);
System.out.printf("Success");
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
// 关闭连接以及 statement
if (statement != null) {
statement.close();
} if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}

上面的插入操作我们看到其所需要的 sql 语句需要的是完整的,当我们插入的值非常多的时候这样拼写 sql 语句就显得有点不适合,所以我们需要去学习 PrepareStatement,它可以用 ? 代表插入值,以及更新和删除操作需要传入的参数,同时也需要利用 setXxx 方法去为每一个 ? 赋值

     @Test
public void testUpdateWithPrepare() {
Connection connection;
PreparedStatement preparedStatement = null; connection = JDBCTools.getConnection(); String sql = "INSERT INTO book (BOOK_NAME, ISBN, PRICE, STOCK) VALUES (?, ?, ?, ?)"; try {
// 获得 prepareStatement 对象
preparedStatement = connection.prepareStatement(sql);
// 为每一列赋值,需要传入下标,从 1 开始
preparedStatement.setString(1, "C#");
preparedStatement.setString(2, "1008");
preparedStatement.setString(3, "320");
preparedStatement.setInt(4, 120);
// 执行更新操作
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCTools.releaseUpdate(preparedStatement, connection);
}

接下来我们介绍如何进行查的操作,首先需要了解 ResultSet 接口(使用完毕需要释放资源)

ResultSet 封装了 JDBC查询的结果集,并返回一张数据表,并有一个指针指向数据表的第一行,我们调用 next() 方法检测下一行是否有效,若为 true 则下移,我们可以利用 getXxx() 方法获取每一行对应的值

@Test
public void testSelect() {
Connection connection = getConnectionMy();
Statement statement = null;
String id = null;
String bookName = null;
String isbn = null;
// 执行查询操作的对象
ResultSet resultSet = null;
String sql = null; try {
sql = "SELECT BOOK_NAME, ISBN, PRICE, STOCK from book WHERE id=5";
// 获取 statement 对象
statement = connection.createStatement();
// 执行查询操作
resultSet = statement.executeQuery(sql); // 处理 resultSet,首先需要判断其是否
if (resultSet.next()) {
// 获得对应的列的值
id = resultSet.getString(1);
bookName = resultSet.getString(2);
isbn = resultSet.getString(3);
} System.out.printf(id + "; " + bookName + "; " + isbn);
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
} if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}

通过上面的方法我们可以体会到每次关闭连接以及以后每次进行相应的操作的时候我们不可能像这样每次都写完整的代码,我们应该像封装获取连接的函数一样去封装其其他方法,写为一个工具类

package jdbc.example.test.myself;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties; public class JDBCTools { /*
* 关闭数据库连接资源
* */
public static void release(Statement statement, Connection connection, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
} if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
} if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} /*
* 获取数据库连接
* */
public static Connection getConnection() {
String user;
String password;
String url; Connection connection = null; InputStream inputStream = JDBCTools.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
try {
properties.load(inputStream); user = properties.getProperty("user");
password = properties.getProperty("password");
url = properties.getProperty("url"); connection = DriverManager.getConnection(url, user, password);
} catch (IOException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} return connection;
} /*
* 执行数据库操作,除查询操作
* */
public static void update(String sql) {
Connection connection;
Statement statement = null; connection = getConnection();
try {
statement = connection.createStatement();
statement.executeUpdate(sql);
} catch (SQLException e) {
e.printStackTrace();
} finally {
releaseUpdate(statement, connection);
}
}
}

现在我们的工具类中并没有通用的查询方法,为了完善我们的工具类我们需要学习 JavaBean 和 ResultSetMetaData

JavaBean 其实就是普通的 java 类,不同的是没有 main 方法,只包含变量和对应的 set、get 方法,数据表对应的类就需要用 JavaBean 去写,其变量名对应数据表的列名,若列名为两个单词那么对应的变量名的第二个单词需大写,如:book_name --> bookName,isbn --> isbn

ResultSetMetaData 是描述 ResultSet 元数据的接口,它可以获取到结果集有多少列,以及列名和列的别名

我们都已经知道 ResultSet 返回的是一张数据表,如果我们还像以前那样在方法中为每一列新建一个变量,就不能完成通用的查询方法,所以我们需要为每张数据表创建一个对应的类,用 JavaBean 的规则。这样 ResultSet 结果集的每一行对应一个对象。

在方法中我们可以利用 ResultSetMetaData 获得结果集中列的别名,以及从结果集中获得对应的值,我们将其存为一个键位列名,值为列值的键值对,方便后面为数据表对应的对象赋值以便打印。

注意:在测试方法中书写 SQL 语句的时候,我们应该向 javaBean 看齐,也就是如果数据表对应的列名为两个单词,那么就应该为其起一个别名,和 JavaBean 对应的变量名统一,如果没有统一,将打印 null!

//   通用的查询方法,clazz 为数据表对应的类
public <T> T get(Class<T> clazz, String sql, Object ... args) {
T entity = null; Connection connection;
PreparedStatement preparedStatement;
ResultSet resultSet;
ResultSetMetaData resultSetMetaData;
// 存储列名以及列值
Map<String, Object> map = new HashMap<String, Object>(); connection = JDBCTools.getConnection();
try {
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(1, args[i]);
}
resultSet = preparedStatement.executeQuery();
resultSetMetaData = resultSet.getMetaData(); if (resultSet.next()) {
for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
map.put(resultSetMetaData.getColumnLabel(i), resultSet.getObject(i));
}
} // 判断 map 中是否有值,若有则利用反射创建对象,并为之赋值
if (map.size() > 0) {
entity = clazz.newInstance();
// 利用for 循环为每一个变量赋值
for (Map.Entry<String, Object> entry: map.entrySet()) {
String fieldName = entry.getKey();
Object fieldValue = entry.getValue(); Field field = clazz.getDeclaredField(fieldName);
// 打破封装
field.setAccessible(true);
field.set(entity, fieldValue);
}
} System.out.printf(String.valueOf(entity));
} catch (SQLException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} return entity;
}
@Test
public void testSelect() {
String sql = "SELECT id, BOOK_NAME bookName, isbn, price, stock FROM book WHERE id = ?";
// testSelect(SH_DB.class, sql, 5);
get(SH_DB.class, sql, 6);
}
package com.jdbc.dao.my.first.test;

/**
* Created by shkstart on 2017/10/31.
*/
public class SH_DB {
private String bookName;
private String isbn;
private int price;
private int stock; public SH_DB() {
} public SH_DB(String bookName, String isbn, int price, int stock) {
this.bookName = bookName;
this.isbn = isbn;
this.price = price;
this.stock = stock;
} public String getBookName() {
return bookName;
} public void setBookName(String bookName) {
this.bookName = bookName;
} public String getIsbn() {
return isbn;
} public void setIsbn(String isbn) {
this.isbn = isbn;
} public int getPrice() {
return price;
} public void setPrice(int price) {
this.price = price;
} public int getStock() {
return stock;
} public void setStock(int stock) {
this.stock = stock;
}
// 为了方便打印重写 toString 方法
@Override
public String toString() {
return "SH_DB{" +
"bookName='" + bookName + '\'' +
", isbn='" + isbn + '\'' +
", price=" + price +
", stock=" + stock +
'}';
}
}

对应的 JavaBean

上面我们利用反射的方式对数据表对象进行了赋值操作,我们还可以利用一个 beanUtils (在使用之前必须导入 beanUtils jar 包和它所依赖的 commons-logging jar 包)工具类为其赋值   BeanUtils.setProperty(class, fieldName, fieldValue);

现在我们可以将工具类方法进行完善以及将其进行重构,包括了查询多条记录,查询单条记录,查询单个值,更新等操作,如下:

package com.jdbc.dao.my.first.test;

import org.apache.commons.beanutils.BeanUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.*; /**
* Created by shkstart on 2017/10/31.
*/ /*
* Dao -> Data Access Object
* 包括了对数据库 CRUD (Create read update delete) 操作,而不包含任何业务逻辑信息
* 可以实现功能模块化,便于维护
* */
public class DaoOfMy { // 更新数据库的方法,可能是删除,更新,增加操作
public void update(String sql, Object... args) {
Connection connection;
PreparedStatement preparedStatement = null; connection = JDBCTools.getConnection();
try {
preparedStatement = connection.prepareStatement(sql); for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
} preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCTools.releaseConnection(connection, preparedStatement, null);
}
} // 查询一条记录
public <T> T get(Class<T> clazz, String sql, Object... args) {
T entity = null; Connection connection;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
ResultSetMetaData resultSetMetaData = null;
Map<String, Object> map = new HashMap<String, Object>(); connection = JDBCTools.getConnection();
try {
preparedStatement = connection.prepareStatement(sql); for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
} resultSet = preparedStatement.executeQuery();
resultSetMetaData = resultSet.getMetaData(); if (resultSet.next()) {
for (int i = 0; i < resultSetMetaData.getColumnCount(); i++) {
String colLabel = resultSetMetaData.getColumnLabel(i + 1);
Object colVal = resultSet.getObject(i + 1);
map.put(colLabel, colVal);
}
} if (map.size() > 0) {
entity = clazz.newInstance();
for (Map.Entry<String, Object> entry : map.entrySet()) {
String fieldName = entry.getKey();
Object fieldVal = entry.getValue();
setFieldVal(entity, fieldName, fieldVal);
}
} } catch (SQLException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} finally {
JDBCTools.releaseConnection(connection, preparedStatement, resultSet);
} return entity;
}
//查询多条记录
public <T> List<T> getForList(Class<T> clazz, String sql, Object... args) {
// 存取所有对象
List<T> entities = new ArrayList<T>(); Connection connection;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null; connection = JDBCTools.getConnection();
try {
preparedStatement = connection.prepareStatement(sql); for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
} resultSet = preparedStatement.executeQuery(); // 存取查询到的多条记录
List<Map<String, Object>> listMap = handleResultSetToMapList(resultSet);
// 存取对象集合
entities = changeMapListToBeanList(clazz, listMap); } catch (SQLException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} finally {
JDBCTools.releaseConnection(connection, preparedStatement, resultSet);
} return entities;
} private <T> List<T> changeMapListToBeanList(Class<T> clazz, List<Map<String, Object>> listMap) throws InstantiationException, IllegalAccessException { List<T> entities = new ArrayList<T>();
T entity2;
if (listMap.size() > 0) { Iterator<Map<String, Object>> iterator = listMap.iterator(); while (iterator.hasNext()) {
entity2 = clazz.newInstance();
for (Map.Entry<String, Object> entry : iterator.next().entrySet()) {
String fieldName = entry.getKey();
Object fieldVal = entry.getValue();
setFieldVal(entity2, fieldName, fieldVal);
}
entities.add(entity2);
}
}
return entities;
} // 处理结果集,将查询到的每一条结果集存入到 List 中
private List<Map<String, Object>> handleResultSetToMapList(ResultSet resultSet) throws SQLException { List<Map<String, Object>> mapList = new ArrayList<Map<String, Object>>();
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
// 存取查询结果中的一条记录
Map<String, Object> map;
while (resultSet.next()) {
// 每次都将 map 对象 new 一下,这样每次就是不同的值,不会出现一直存取第一个值
map = new HashMap<String, Object>();
for (int i = 0; i < resultSetMetaData.getColumnCount(); i++) {
String colLabel = resultSetMetaData.getColumnLabel(i + 1);
Object colVal = resultSet.getObject(i + 1);
map.put(colLabel, colVal);
}
mapList.add(map);
}
return mapList;
} public void setFieldVal(Object obj, String fieldName, Object fieldVal) {
// Field field;
try {
// 使用反射赋值
// field = obj.getClass().getDeclaredField(fieldName);
// field.setAccessible(true);
// field.set(obj, fieldVal);
// 使用 BeanUtils 操作类的属性
BeanUtils.setProperty(obj, fieldName, fieldVal);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} // 获取单个值
public int getForValue(String sql) {
Connection connection;
int count = 0;
PreparedStatement preparedStatement;
ResultSet resultSet; connection = JDBCTools.getConnection();
try {
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery(); if (resultSet.next()) {
count = resultSet.getInt(1);
}
} catch (SQLException e) {
e.printStackTrace();
}
return count;
}
}

以上是上一周学习的一部分,剩下的我很快就会发布,希望大家提出自己宝贵的意见,谢谢!