java 数据驱动_你真的了解Java的数据驱动吗:从JDBC说起

数据库访问

我们要写数据库驱动程序,至少得先访问到、连接上数据库吧。

假设我们要写个连接数据库的应用程序,无论是我们要连接本地的数据库,还是远程的数据库,我们要做的事儿,无非就是进程间通信,我们靠OS提供给我们的socket就行啦,这时我们只需要Java和数据库定义一个应用层的协议, 就是所谓的你发什么请求, 我给你什么响应(例如:握手、认证、约定格式等)就行了。

接口的统一

MySQL、Oracle、SQL Server、DB2等等各家数据库,都有自己家的一个应用层访问协议,这就造成一个问题,我们的数据库连接程序只能对应一个数据库,要是从MySQL换到Oracle的话,就GG了,只能重写一套,这是相当麻烦的!!!

更难受的是, 每套代码都得处理非常多的协议细节, 我要是就只是写那么简简单单的一个SQL语句,也要写一堆杂七杂八的!!!问题的关键就在于:直接使用socket编程, 太low 了 , 必须得有一个抽象层来屏蔽这些细节!

于是乎Java想出了Connection这么个抽象玩意儿来代表连接,Statement来表示SQL语句,ResultSet 表示返回结果。并且,他们都是接口!!!具体怎么实现,按照具体数据库来,而其中那些实现的代码就需要处理那些烦人的细节了!!!

于是乎,这个玩意儿,就被叫做JDBC,Java自己定义一个标准化接口,丢出去,活让别人干去

面向接口编程

假的抽象编程

有了接口了,数据库也给咱提供具体实现的jar包了,那咱们怎么个写法呢?

Connection conn = new MysqlConnectionImpl("localhost","3306","stu_db","root","admin");

要是这么写,那就糟糕了啊!

?看起来没什么问题啊?很有问题啊!

要是我jar包升级,并且把类名改为MysqlConnectionJDBC4Impl咋办?那代码不就GG了,这哪是面向接口编程啊,这不还是面向具体吗,没碰到本质问题上。

想想设计模式,我们是不是应该把对象创建的具体实现封装起来,别让用户自己new!想想这对应什么模式呢?——工厂模式

新的一层抽象

Java冥思苦想,类比了一下我们的计算机,I/O设备都需要驱动才能使用,那我能不能把数据库也当成I/O设备,抽象出一个驱动作为中间层呢?我们来模拟一下:(Properties是一个配置类)

public class Driver{

public static Connection getConnection(String dbType,Properties info){

if("mysql".equals(dbType)){

return new MysqlConnectionImpl(info);

}

if("oracle".equals(dbType)){

return new OracleConnectionImpl(info);

}

if("db2".equals(dbType)){

return new DB2ConnectionImpl(info);

}

throw new RuntimeException("unsupported db type = " + dbType);

}

}

我们用简单工厂实现了,那我们再来看看怎么用吧!

Properties info = new Properties();

info.put("host","localhost");

info.put("port","3306");

// 配置一堆玩意儿...

Connection conn = Driver.getConnection("mysql",info);

这不就拿到Connection接口了吗?面向抽象,永远嘀神!

等等,不对啊,问题还是没解决啊,我如果要增加数据库,或者修改连接类的类名,还是要去改Driver的代码啊喂,那咋办嘛?

数据驱动

为了实现彻底解耦,我们可以把数据库驱动所需class的全限定类名写在配置文件里,通过I/O去读配置文件内容就好啦!这样就不用去修改代码了,直接修改配置文件就行,不然程序还得重新编译运行,头疼啊!

mysql = com.mysql.jdbc.MysqlConnectionImpl

db2 = com.ibm.db2.DB2ConnectionImpl

oracle = com.oracle.jdbc.OracleConnection

sqlserver = com.Microsoft.jdbc.SqlServerConnection

这时候,我们作为用户,只要配合一波反射,就能在程序中动态生成Connection类辽~

public class Driver{

public static Connection getConnection(String dbType,Properties info){

Class> clz = getConnectionImplClass(dbType);

try{

Constructor> c = clz.getConstructor(Properties.class);

return (Connection) c.newInstance(info);

} catch (Exception e) {

e.printStackTrace();

return null;

}

}

private static Class> getConnectionImplClass(String dbType){

// 读取配置文件,从中根据dbType来读取相应的Connection实现类

}

}

这样是不是优雅了很多呢?

但是!还有两个问题:

客户使用时,还需要提供一个配置文件,且配置文件还要把具体实现类写对才行

创建实现类的过程被暴露出来了,我们用户竟然还要自己反射,不能这样!要让各个数据库厂商在各自的jar包里创建自家的Connection实例对象

工厂方法

为了解决上述问题,我们决定用更高级的工厂方法,而非简单工厂

// 属于jdk的Driver类

public interface Driver {

public Connection getConnection(Properties info);

}

// 属于mysql-jdbc.jar的MysqlDriver类

public class MysqlDriver implements Driver {

public Connection getConnection(Properties info) {

return new MysqlConnectionImpl(info);

}

}

// 属于oracle-jdbc.jar的OracleDriver类

public class OracleDriver implements Driver {

public Connection getConnection(Properties info) {

return new OracleConnectionImpl(info);

}

}

// ...

你有没有疑问,我这样是不是引入了新的问题,我难不成还得new出来?

Driver driver = new MysqlDriver();

Connection conn = driver.getConnection(info);

不不不,我们还是继续反射就行,让它动态创建

Class> clz = Class.forName("com.mysql.MysqlDriver");

Driver driver = (Driver) clz.newInstance();

Connection conn = driver.getConnection(info);

啊这,一直说反射,但我不会啊...

得,咱们继续简化!

// 驱动管理

public class DriverManager {

// 驱动注册表

private static List registeredDrivers = new ArrayList<>();

// 通过配置,获得连接

public Connection getConnection(String url,String user,String pswd) {

Properties info = new Properties();

info.put("user",user);

info.put("pswd",pswd);

for(Driver driver : registeredDrivers){

Connection conn = driver.getConnection(url,info);

if(conn != null) {

return conn;

}

}

throw new RuntimeException("Connection Failed!");

}

// 不存在注册表里,就注册驱动

public static void register(Driver driver){

if(!registeredDrivers.contains(driver)){

registeredDrivers.add(driver);

}

}

}

【额外说一句,当类被加载到jvm里,静态成员变量和静态代码块会先执行】

我们再来看看Mysql的具体驱动现在要怎么写

public class MysqlDriver implements Driver {

// 在Mysql驱动类被装载时,就注册到DriverManager的注册表中

static{

DriverManager.register(new MysqlDriver());

}

// 获取具体连接

public Connection getConnection(String url,Properties info) {

if(acceptsURL(url)){

return new MysqlConnectionImpl(info);

}

return null;

}

// 格式检查

public boolean acceptsURL(String url){

return url.startsWith("jdbc:mysql");

}

}

芜湖~起飞!✈️

快来看看怎么用的!

Class.forName("com.mysql.MysqlDriver");

Connection conn = DriverManager.getConnection(

"jdbc:mysql://localhost:3306/studb",

"root",

"admin");

可以吧!是不是有内味了!我们来复盘一下:

装载:首先把Mysql的具体驱动类MysqlDriver(class类模板)通过反射加载到jvm里

注册驱动:装载后,MysqlDriver执行静态代码块,new了一个MysqlDriver的实例对象,注入DriverManager的注册方法,完成注册

获取连接:根据配置信息,返回连接对象

【关于Class.forName,其实在JDBC4.0之后的规范是不需要写的,但为了兼容老版本的JDBC规范,还是写上比较好,强制加载,保证不出错】

到这里,我们就完成了数据库的连接啦!一套体系就出来辽!!!

ORM的出现

又臭又长的JDBC

看似问题解决了,但是开发者们似乎还是有很多的抱怨,我就是一个简单的select * from也要写一堆,不信你瞧:

package com.microsoft.jdbc;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

import java.sql.Statement;

public class JDBCDemo {

public static void main(String[] args){

try {

//1.加载驱动

Class.forName("com.mysql.cj.jdbc.Driver");

//2.获取连接对象

String url = "jdbc:mysql://localhost:3306/how2java?useUnicode=true&characterEncoding=UTF-8";

Connection connection = DriverManager.getConnection(url,"root","admin");

//3.定义sql语句

String sql = "select * from account";

//4.获取执行sql语句的表单对象

Statement statement = connection.createStatement();

//5.执行sql

ResultSet resultSet = statement.executeQuery(sql);

//6.处理结果

while(resultSet.next()){

int id = resultSet.getInt("id");

String name = resultSet.getString("name");

Double money = resultSet.getDouble("money");

System.out.println(id+" "+name+" "+money);

}

//7.释放资源

statement.close();

connection.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

啊这,好像是这么回事,那咋办嘛?

JDBC模板出现

仔细想想,其实数据库访问无外乎这几件事情:

指定数据库连接参数

打开数据库连接

声明SQL语句

预编译并执行SQL语句

遍历查询结果

处理每一次遍历操作

处理抛出的任何异常

处理事务

关闭数据库连接

那我们是不是可以尝试写一个JDBC的模板?比如这样:

List users = this.jdbcTemplate.query(

"select id,name from users",

new RowMapper(){

public User mapRow(ResultSet rs,int rowNum) throws SQLException{

User user = new User();

user.setID(rs.getInt("id"));

user.setName(rs.getString("name"));

return user;

}

}

);

这样一来,就使得我们更加专注于业务,而非连接的创建上!

可问题是你this.jdbcTemplate这个对象哪里来的啊?这个问题不大:

// 获取数据源(伪代码)

DataSource ds = Tool.getDataSource();

this.jdbcTemplate = new JdbcTemplate(ds);

我们只要把数据源注入JDBC模板中就行啦!

JDBC模板这样对JDBC进行封装 ,的确把数据库的访问向前推进了一大步,但是我们的本质的问题仍然没有解决!

什么本质问题?

这个问题就是面向对象世界和关系数据世界之间存在的巨大鸿沟。

就比如说,ResultSet依然是对一个表的数据的抽象和模拟:rs.next() 获取下一行,rs.getXXX() 访问该行某一列;把关系数据转化成Java对象的过程,仍然需要码农们写大量代码来完成!

这时候救星出现了——我们的主角,ORM!!!

ORM救星的到来

啥是ORM呢?别被它洋气的名字吓到了!其实就是对象关系映射(Object Relational Mapping)

啥是对象?自然是指Java对象了嘛

啥是关系?自然是SQL数据库的一张张表了嘛

我们约定几个原则:

数据库的表映射为Java 的类(class)

表中的行记录映射为一个个Java 对象

表中的列映射为Java 对象的属性

都是一一对应的嗷~

但咱们说是这么说,但实际操作起来就遇到了一堆麻烦,咱随便说几个:

Java类的粒度要精细的多, 有时候多个类合在一起才能映射到一张表

SQL没有继承一说

对象标识不同,Java用==或者是equals,而SQL用的是主键

对象之间互相关联依赖的问题,SQL只能外键、关联表了

Java中数据导航容易,比如City c = user.getAddress().getCity();,但是SQL就得使用上连接

Java中的对象无非就是要用创建,不用回收;但是涉及到数据库,就得考虑持久化状态(是否写入磁盘)

等等等等!!!!!!!!

所以我们要感谢ORM框架的开发者们啊!!!

消除了 90%以上的JDBC API代码

SQL从Java代码中解耦出来,写到xml配置中,可复用,可读性好

数据类型转换的自动化

推荐视频🔗

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值