一、概述
1.1 简介
- JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准Java类库,(java.sql,javax.sql)使用这些类库可以以一种标准的方法、方便地访问数据库资源。
- JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。
- JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程
?1.2 JDBC体系结构
????????JDBC接口(API)包括两个层次:
- ????????面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。
- ????????面向数据库的API:Java Driver API,供开发商开发数据库驱动程序用。
1.3 编写步骤
?1.4 连接数据库方式举例(依次递进到最终版)
//8.0.26
import org.junit.Test;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class ConnectionTest {
//方式1
@org.junit.Test
public void testConnection1() throws SQLException {
Driver driver = new com.mysql.cj.jdbc.Driver();//获取Driver的实现类对象
String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8";
Properties info = new Properties();//封装用户名和密码
info.setProperty("user","root");
info.setProperty("password","12345");
Connection connect = driver.connect(url, info);
System.out.println(connect);
}
//方式2:对方式1的迭代
//不出现第三方API,使程序具有更好的可移植性。如:new com.mysql.cj.jdbc.Driver();
@Test
public void testConnection2() throws Exception {
//获取Driver的实现类对象【使用反射】
Class aClass = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver) aClass.getDeclaredConstructor().newInstance();
String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8";
Properties info = new Properties();//封装用户名和密码
info.setProperty("user","root");
info.setProperty("password","12345");
Connection connect = driver.connect(url, info);
System.out.println(connect);
}
//方式3:使用DriverManager替换Driver
@Test
public void testConnection3() throws Exception {
//获取Driver的实现类对象【使用反射】
Class aClass = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver) aClass.getDeclaredConstructor().newInstance();
String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8";
String user = "root";
String password = "12345";
//注册驱动
DriverManager.registerDriver(driver);
//获取连接
Connection connect = DriverManager.getConnection(url,user,password);
System.out.println(connect);
}
//方式4:优化3,不用显式地注册驱动
@Test
public void testConnection4() throws Exception {
String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8";
String user = "root";
String password = "12345";
//获取Driver的实现类对象【使用反射】
//加载Driver
Class.forName("com.mysql.cj.jdbc.Driver");//静态代码块,自动注册驱动
// Driver driver = (Driver) aClass.getDeclaredConstructor().newInstance();
// //注册驱动
// DriverManager.registerDriver(driver);
//获取连接
Connection connect = DriverManager.getConnection(url,user,password);
System.out.println(connect);
}
//方式5【最终版】:将数据库连接需要的4个信息放在配置文件中,通过读取配置文件获取连接
//实现数据和代码分离,解耦!
//修改连接信息后避免程序重新打包
@Test
public void testConnection5() throws Exception {
//读取配置文件中的信息
InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
//加载驱动
Class.forName(driverClass);
//获取连接
Connection connect = DriverManager.getConnection(url,user,password);
System.out.println(connect);
}
}
jdbc.properties文件内容:
user=root
password=12345
url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8
driverClass=com.mysql.cj.jdbc.Driver
二、?使用PreparedStatement实现CRUD操作
2.1 操作和访问数据库
- 数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果。其实一个数据库连接就是一个Socket连接。
- 在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:
- ??????? Statement:用于执行静态 SQL 语句并返回它所生成结果的对象。
- ??????? PrepatedStatement:SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句。
- ??????? CallableStatement:用于执行 SQL 存储过程
2.2? 使用Statement操作数据表的弊端
??????? 1. 通过调用 Connection 对象的 createStatement() 方法创建该对象。该对象用于执行静态的 SQL 语句,并且返回执行结果。 ??????? 2. Statement 接口中定义了下列方法用于执行 SQL 语句
int excuteUpdate(String sql):执行更新操作INSERT、UPDATE、DELETE ResultSet executeQuery(String sql):执行查询操作SELECT
???????? 3. 使用Statement操作数据表存在弊端:
- ????????????????存在拼串操作,繁琐
- ????????????????存在SQL注入问题
????????SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令(如:SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' ='1') ,从而利用系统的 SQL 引擎完成恶意行为的做法。 ????????对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement(从Statement扩展而来) 取代 Statement 就可以了。
???????? 4. 代码演示
public class User {
private String user;
private String password;
public User() {
}
public User(String user, String password) {
super();
this.user = user;
this.password = password;
}
@Override
public String toString() {
return "User [user=" + user + ", password=" + password + "]";
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.Scanner;
import org.junit.Test;
public class StatementTest {
public static void main(String[] args) {
testLogin();
}
// 使用Statement的弊端:需要拼写sql语句,并且存在SQL注入的问题
public static void testLogin() {
Scanner scan = new Scanner(System.in);
System.out.print("用户名:");
String userName = scan.nextLine();
System.out.print("密 码:");
String password = scan.nextLine();
// SELECT user,password FROM user_table WHERE USER = '1' or ' AND PASSWORD = '
// ='1' or '1' = '1';
String sql = "SELECT user,password FROM user_table WHERE USER = '" + userName + "' AND PASSWORD = '"
+ password + "'";
User user = get(sql, User.class);
if (user != null) {
System.out.println("登录成功!");
} else {
System.out.println("用户名或密码错误!");
}
}
// 使用Statement实现对数据表的查询操作
public static <T> T get(String sql, Class<T> clazz) {
T t = null;
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
// 1.加载配置文件
InputStream is = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
// 2.读取配置信息
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
// 3.加载驱动
Class.forName(driverClass);
// 4.获取连接
conn = DriverManager.getConnection(url, user, password);
st = conn.createStatement();
rs = st.executeQuery(sql);
// 获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
// 获取结果集的列数
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
// //1. 获取列的名称
// String columnName = rsmd.getColumnName(i+1);
// 1. 获取列的别名
String columnName = rsmd.getColumnLabel(i + 1);
// 2. 根据列名获取对应数据表中的数据
Object columnVal = rs.getObject(columnName);
// 3. 将数据表中得到的数据,封装进对象
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, columnVal);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return null;
}
}
|