jdbcmysql源码_源码详解系列(二)------jdbc-mysql的使用和分析
jdbcmysql源码_源码详解系列(⼆)------jdbc-mysql的使⽤和
分析
简介
什么是JDBC
JDBC是⼀套连接和操作数据库的标准、规范。通过提供DriverManager、Connection、Statement、ResultSet等接⼝将开发⼈员与数据库提供商隔离,开发⼈员只需要⾯对JDBC接⼝,⽆需关⼼怎么跟数据库交互。
⼏个重要的类
类名
作⽤
DriverManager
驱动管理器,⽤于注册驱动,是获取 Connection对象的⼊⼝
Driver
数据库驱动,⽤于获取Connection对象
Connection
数据库连接,⽤于获取Statement对象、管理事务
Statement
sql执⾏器,⽤于执⾏sql
ResultSet
结果集,⽤于封装和操作查询结果
prepareCall
⽤于调⽤存储过程
使⽤中的注意事项
记得释放资源。另外,ResultSet和Statement的关闭都不会导致Connection的关闭。
maven要引⼊oracle的驱动包,要把jar包安装在本地仓库或私服才⾏。
使⽤PreparedStatement⽽不是Statement。可以避免SQL注⼊,并且利⽤预编译的特点可以提⾼效率。
使⽤例⼦
需求
使⽤JDBC对mysql数据库的⽤户表进⾏增删改查。
⼯程环境
JDK:1.8
maven:3.6.1
IDE:sts4
mysql driver:8.0.15
mysql:5.7
主要步骤
⼀个完整的JDBC保存操作主要包括以下步骤:
注册驱动(JDK6后会⾃动注册,可忽略该步骤);
通过DriverManager获得Connection对象;
开启事务;
通过Connection获得PreparedStatement对象;
设置PreparedStatement的参数;
执⾏保存操作;
保存成功提交事务,保存失败回滚事务;
释放资源,包括Connection、PreparedStatement。
创建表
CREATE TABLE `demo_user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '⽤户id',
`name` varchar(16) COLLATE utf8_unicode_ci NOT NULL COMMENT '⽤户名',悠组词语
`age` int(3) unsigned DEFAULT NULL COMMENT '⽤户年龄',
`gmt_create` datetime DEFAULT NULL COMMENT '记录创建时间',
`gmt_modified` datetime DEFAULT NULL COMMENT '记录最近修改时间',
`deleted` bit(1) DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_name` (`name`),
KEY `index_age` (`age`)
)
ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
北京内衣品牌创建项⽬
项⽬类型Maven Project,打包⽅式jar
引⼊依赖
junit
junit
油烟机排行榜
4.12
test
mysql
mysql-connector-java
8.0.15
注意:由于oracle商业版权问题,maven并不提供Oracle JDBC driver,需要将驱动包⼿动添加到本地仓库或私服。编写jdbc.prperties
下⾯的url拼接了好⼏个参数,主要为了避免乱码和时区报错的异常。
路径:resources⽬录下
sql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/github_demo?
useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true
#这⾥指定了字符编码和解码格式,时区,是否加密传输
username=root
password=root
#注意,xml配置是&采⽤&替代
如果是oracle数据库,配置如下:
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@//localhost:1521/xe
username=system
password=root
获得Connection对象
private static Connection createConnection() throws Exception {
// 导⼊配置⽂件
Properties pro = new Properties();
InputStream in = ClassLoader().getResourceAsStream( "jdbc.properties" );
Connection conn = null;
pro.load( in );
/
/ 获取配置⽂件的信息
String driver = Property( "driver" );
String url = Property( "url" );
String username = Property( "username" );
String password = Property( "password" );
// 注册驱动,JDK6后不需要再⼿动注册,DirverManager的静态代码块会帮我们注册
// Class.forName(driver);
// 获得连接
conn = Connection( url, username, password );
return conn;
}
使⽤Connection对象完成保存操作
这⾥简单地模拟实际业务层调⽤持久层,并开启事务。另外,获取连接、释放资源可以通过⾃定义的⼯具类 JDBCUtil 来实现,具体见源码。
@Test
public void save() throws Exception {
UserDao userDao = new UserDaoImpl();
// 创建⽤户
User user = new User("zzf002", 18, new Date(), new Date());
try (Connection connection = Connection()) {
// 开启事务
connection.setAutoCommit(false);
// 保存⽤户
userDao.insert(user);
// 提交事务
connectionmit();
}
}
接下来看看具体的保存操作,即DAO层⽅法。
public void insert(User user) throws Exception {
String sql = "insert into demo_user (name,age,gmt_create,gmt_modified) values(?,?,?,?)"; Connection connection = Connection();
// 获取PreparedStatement对象
PreparedStatement prepareStatement = connection.prepareStatement(sql);
// 设置参数
prepareStatement.setString(1, Name());
prepareStatement.setInt(2, Age());
prepareStatement.setDate(3, new java.sql.Gmt_create().getTime())); prepareStatement.setDate(4, new java.sql.Gmt_modified().getTime()));
// 执⾏保存
// 释放资源北方
}
源码分析
驱动注册
DriverManager主要⽤于管理数据库驱动,并为我们提供了获取连接对象的接⼝。其中,它有⼀个重要的成员属性registeredDrivers,是⼀个CopyOnWriteArrayList集合(通过ReentrantLock实现线程安全),存放的是元素是DriverInfo对象。
//存放数据库驱动包装类的集合(线程安全)
private final static CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList<>();
public static synchronized void registerDriver(java.sql.Driver driver)
throws SQLException {
//调⽤重载⽅法,传⼊的DriverAction对象为null
registerDriver(driver, null);
}
public static synchronized void registerDriver(java.sql.Driver driver,
DriverAction da)
throws SQLException {
if(driver != null) {
//当列表中没有这个DriverInfo对象时,加⼊列表。张爱朋微博
//注意,这⾥判断对象是否已经存在,最终⽐较的是driver地址是否相等。
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
throw new NullPointerException();
}
println("registerDriver: " + driver);
}
为什么集合存放的是Driver的包装类DriverInfo对象,⽽不是Driver对象呢?
通过DriverInfo的源码可知,当我们调⽤equals⽅法⽐较两个DriverInfo对象是否相等时,实际上⽐较的是Driver对象的地址,也就是说,我可以在DriverManager中注册多个MYSQL驱动。⽽如果直接存放的是Driver对象,就不能达到这种效果(因为没有遇到需要注册多个同类驱动的场景,所以我暂时理解不了这样做的好处)。
DriverInfo中还包含了另⼀个成员属性DriverAction,当我们注销驱动时,必须调⽤它的deregister⽅法后才能将驱动从注册列表中移除,该⽅法决定注销驱动时应该如何处理活动连接等(其实⼀般在构造DriverInfo进⾏注册时,传⼊的DriverAction对象为空,根本不会去使⽤到这个对象,除⾮⼀开始注册就传⼊⾮空DriverAction对象)。
综上,集合中元素不是Driver对象⽽DriverInfo对象,主要考虑的是扩展某些功能,虽然这些功能⼏乎不会⽤到。
注意:考虑篇幅,以下代码经过修改,仅保留所需部分。
class DriverInfo {
final Driver driver;
DriverAction da;
国培研修总结
DriverInfo(Driver driver, DriverAction action) {
this.driver = driver;
da = action;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。