赞
踩
注册JDBC驱动的三种方式(以mysql为例):
public class Driver extends com.mysql.cj.jdbc.Driver {
public Driver() throws SQLException {
}
static {
System.err.println("Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.");
}
}
在加载类时会执行静态代码块中的类容,所以我们会在命令行中看到
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
com.mysql.jdbc.Driver
继承了com.mysql.cj.jdbc.Driver
,com.mysql.cj.jdbc.Driver
类容如下
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
可以看到,在加载com.mysql.cj.jdbc.Drive
时会调用DriverManager.registerDriver(new Driver())
,这正是方法2的实现方式。但二者的效果并不相同,方法1只是在类加载的时候调用了这段代码,因此只创建了一个Driver()
对象。然而,方法2显式的执行了DriverManager.registerDriver(new Driver())
,这时会注册一个Driver
对象,在执行new Driver()
会先加载Driver类,然后会执行静态代码块中类容也就是DriverManager.registerDriver(new Driver())
,又注册了一个Driver对象。方法3实现比较复杂,但通过断点调试可以发现其作用与方法1类似。
数据库连接可以通过以下方式创建
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai","root","root");
上面注册了JDBC驱动,下面获取JDBC连接时似乎没有使用上面注册时的信息,那注册JDBC驱动又有什么意义呢?
事实上,JDBC只是一套规范,需要各个数据库厂商(如MySQL,Oracle等)自己去实现。而DriverManager属于import java.sql
不依赖于任何数据库厂商,理论上不可能获得具体数据库的连接。那这只有一个可能,DriverManager维护了一个Driver对象,在注册Driver对象时,将具体厂商实现的对象赋值给了DriverManager维护的Driver对象,在调用DriverManager.getConnection
时其实调用了Driver对象的getConnection方法。通过分析源代码,我们的猜想得到了验证。
在DriverManager对象中维护了一个存储DriverInfo
对象的list。
public class DriverManager {
// List of registered JDBC drivers
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
DriverInfo
对象的维护了一个Driver
对象。
class DriverInfo {
final Driver driver;
DriverAction da;
DriverInfo(Driver driver, DriverAction action) {
this.driver = driver;
da = action;
}
再来看一下DriverManager.registerDriver(new Driver())
方法
public static synchronized void registerDriver(java.sql.Driver driver)
throws SQLException {
registerDriver(driver, null);
}
他调用了registerDriver
方法,registerDriver
实现如下
public static synchronized void registerDriver(java.sql.Driver driver,
DriverAction da)
throws SQLException {
/* Register the driver if it has not already been added to our list */
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
println("registerDriver: " + driver);
}
我们发现他其实就是向registeredDrivers
列表中添加了一个DriverInfo对象。
再看一下DriverManager.getConnection
方法,
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}
他调用了另一个重载的getConnection方法,具体实现中最重要的一段代码如下
for(DriverInfo aDriver : registeredDrivers) { // If the caller does not have permission to load the driver then // skip it. if(isDriverAllowed(aDriver.driver, callerCL)) { try { println(" trying " + aDriver.driver.getClass().getName()); Connection con = aDriver.driver.connect(url, info); if (con != null) { // Success! println("getConnection returning " + aDriver.driver.getClass().getName()); return (con); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } } else { println(" skipping: " + aDriver.getClass().getName()); } }
这段代码将registeredDrivers
队列中的最后一个DriverInfo
中的Driver对象的connect(url, info)
返回值作为DriverManager.getConnection
方法的返回值。
这时候我们就不难理解DriverManager.getConnection为什么是这串类容了"jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai","root","root"
。每个厂商的URL命名规则都有可能不同,不论字符串的内容是什么,只要遵守对应的命名规则即可,因为最终还要交由对应的JDBC实现来解析。MySQL的JDBC链接字符串格式是jdbc:mysql://主机ip:端口/数据库名?参数1=值1&参数2=值2
Statement stmt = conn.createStatement();
(1)执行查询
ResultSet set = stmt.executeQuery(String sql);
遍历结果
while (resultSet.next()){
Integer eno = resultSet.getInt("eno"); //获取整型
String ename = resultSet.getString("ename"); //获取String类型
Date hiredate = resultSet.getDate("hiredate"); //获取Date类型
}
(2)执行插入操作
Statement stmt = conn.createStatement();
int n = stmt.executeUpdate("insert into employee(eno, ename, salary, dname) values(1011,'员工1011',5000,'市场部') ");
(3)执行更新操作
Statement stmt = conn.createStatement();
int n = stmt.executeUpdate("update employee set dname = '市场营销' where ename = '张三'");
(4)删除操作
Statement stmt = conn.createStatement();
int n = stmt.executeUpdate("delete from employee where ename = '张三'");
PreparedStatement
防止SQL注入攻击String sql = "select * from employee where dname = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1,"市场部");
ResultSet resultSet = pstmt.executeQuery();
(2)执行插入操作
String sql = "insert into employee(eno, ename, salary, dname) values(?, ?, ?, ?) ";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1,1012);
pstmt.setString(2, "员工1012");
pstmt.setInt(3, 5000);
pstmt.setString(4,"营销部");
pstmt.executeUpdate();
(3)执行更新操作
String sql = "update employee set dname = '市场营销' where ename = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "王五");
int n = pstmt.executeUpdate();
(4)删除操作
String sql = "delete from employee where ename = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1,"王五");
int n = pstmt.executeUpdate();
conn.setAutoCommit(false)
关闭事务自动开启,然后手动控制事务的提交时机。conn.setAutoCommit(false);//关闭事务自动提交
conn.commit();//提交事务
conn.rollback();//事务回滚
在使用conn.commit()
提交事务前,事务是写在事务区中,并没有更新数据库,如果事务执行失败conn.commit()
不会执行到,事务也就无法被提交。必须要注意的是,事务提交失败事务区中的类容并没有被删除,再次提交仍然能够修改数据库,因此需要使用conn.rollback();
对事务进行回滚,删除事务区中的内容。
4.数据库中的时间对象
MySQL中的时间类型有Date和DateTime等,Date精确到天,DateTime精确到秒。可以使用"YYYY-MM-DD"或者"YYYY-MM-DD HH:MM:SS"进行对应的时间输入,也可以使用YYYYMMDD或者YYYYMMDDHHMMSS(注意是一个整数而不是字符串),还可以使用now()获取当前时间。在JDBC中可以,可以使用String字符串或者数字或者sql.Date()对象设置时间,获取时间时只能使用得到String或者sql.Date()对象。
使用Statement
Statement stmt = conn.createStatement();
int n = stmt.executeUpdate("update employee set hiredate = '2020-09-07 16:08:30' where eno = 1012");
//int n = stmt.executeUpdate("update employee set hiredate = 20200907160830 where eno = 1012");
ResultSet resultSet = stmt.executeQuery("select * from employee where eno = 1012");
resultSet.next();
System.out.println(resultSet.getInt("hiredate"));
//System.out.println(resultSet.getString("hiredate"));
使用PreparedStatement
String sql = "update employee set hiredate = ? where eno = 1012";
PreparedStatement pstmt = conn.prepareStatement(sql);
//pstmt.setString(1,"2020-07-09");
//pstmt.setInt(1,20200708);
Date date = new Date();
long time = date.getTime();
pstmt.setDate(1, new java.sql.Date(time));
pstmt.executeUpdate();
需要注意的是,resultSet.getInt(“hiredate”)返回的是java.sql.Date
,pstmt.setDate()
也需要传入java.sql.Date
。
2. 批处理
String sql = "insert into employee(eno, ename, salary, dname) values(?, ?, ?, ?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt = conn.prepareStatement(sql);
for(int i = 200; i < 300; i++){
pstmt.setInt(1, i);
pstmt.setString(2, "员工" + i);
pstmt.setInt(3, 5000);
pstmt.setString(4, "员工" + i);
pstmt.addBatch();
}
pstmt.executeBatch();
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <!-- 这是默认配置信息 --> <default-config> <!-- 连接四大参数配置 --> <property name="jdbcUrl">jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai</property> <property name="driverClass">com.mysql.cj.jdbc.Driver</property> <property name="user">root</property> <property name="password">root</property> <!-- 池参数配置 --> <property name="acquireIncrement">3</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">2</property> <property name="maxPoolSize">10</property> </default-config> </c3p0-config>
获取数据库连接
DataSource dataSource = new ComboPooledDataSource();
Connection conn = dataSource.getConnection();
(2)druid连接池
在资源文件夹下创建druid-config.properties文件
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username=root
password=root
initialSize=10
maxActive=20
获取数据库连接
Properties properties = new Properties();
String propertyFile = StandardJDBCExample.class.getResource("/druid-config.properties").getPath();
propertyFile = URLDecoder.decode(propertyFile, "UTF-8");
properties.load(new FileInputStream(propertyFile));
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
DataSource dataSource = new ComboPooledDataSource();
QueryRunner qr = new QueryRunner(dataSource);
try {
List<Employee> list = qr.query("select * from employee where salary > ?",
new BeanListHandler<Employee>(Employee.class),
new Object[]{4000});
for(Employee employee : list)
System.out.println(employee.getDname());
} catch (SQLException e) {
e.printStackTrace();
}
(2)更新操作
DataSource dataSource = new ComboPooledDataSource(); Connection conn = null; try { conn = dataSource.getConnection(); conn.setAutoCommit(false); QueryRunner qr = new QueryRunner(dataSource); qr.update("update employee set salary = 6000 where salary = ?", new Object[]{3500}); conn.commit(); } catch (SQLException e) { try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); }finally { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。