持久化
目前广泛使用的应该还是mysql,nosql。对于NoSQL目前我还没有太多的认识,长路漫漫,慢慢学习中…
在Java中,JDBC应该是被大家摒弃的,因为如果真的采用JDBC去写代码,太多模版代码,有点洁癖的程序猿估计都不想看到自己的代码冗余,会想方设法的去减少模版代码,这个时候就有太多的工具了,意图减少此类模板代码(其实主要的模版代码是建立connection,组装sql,获取结果集,组装POJO,顺便说明一下,有人主张划分VO,DTO,POJO,经过我自己写代码,有好处有不好的地方,看自己的偏好吧),spring jdbctemplate(技术应该已经过时)、hibernate、mybatis、JSR-220、spring data…东西太多了,我知道的太有限,但是估计大家都知道,这些框架,或者工具的简历都离不开JDBC,由此可见JDBC其实还是很重要的,虽然这些工具简化了数据库的操作,但是如果想知道为什么这么设计,了解JDBC你就知道了~~~
数据库框架选择
这个问题其实我在网上查了很久,翻了一下我的第一条查询纪录,大概是四月份,在两个月之前我就开始查了, 一直没找到合适的解释,不过我还是想要说说我自己的看法.
JDBC 大家都知道它是啥玩意,又各个厂商开发提供实现,Java SE制定了相关的规范 JPA JSR-220,在众多框架的发展之后,JCP意识到应该有一个规范了,于是JPA诞生了 hibernate 这个不用多说了,大家都懂 mybatis 好用,简单,学习成本低 spring data 目标在于对JPA再做一次减法
对于以上内容,估计大部分公司都会做出自己的选择,我也在网上咨询了很多扣扣群,问了群里的大神们自己对数据库框架的选型,其实貌似也没告诉我为什么。一下就是我对框架的看法:
对于这么多框架,其实主要使用的就是hibernate、mybatis。spring data的使用应该还比较少。我问了我很多同事,大家都说hibernate太重,其实功能强大为什么不重呢…mybatis大家可以定制sql,扪心自问,我们有多少数据库查询需要这么care数据库性能。如果我来制定数据库技术方案,我肯定优先使用spring data + JPA + hibernate,实在不行再采用spring + mybatis,原因如下:我自己做事情比较喜欢标准化,因为JPA是标准,而hibernate则很好的支持了JPA,spring data有效的减少了模版代码的产生,加上spring data 可以与批处理框架结合使用,因此我觉得使用它真的是不二之选
JDBC
大部分网上的资料对JDBC的介绍过于简单,只提及了JDBC都不算主要功能的功能。在我看来,JDBC分为以下几部分:
数据库驱动,大家熟悉的DriverManager Connection Statement(包含很多种,下文会依次介绍) ResultSet Transactions 异常 RowSet
驱动
为了获取链接,我们必须要有驱动,驱动其实就是数据库厂商为了适配JDBC而对自己的数据库客户端做的初始化而已,在JDBC 4.0之前我们需要这样初始化:
1 | Class.forName( "com.mysql.jdbc.Driver" );
|
在JDBC 4.0以后我们将不在需要这行代码。
按照oracle的划分,数据库驱动分为四大类:
做接口映射的驱动,这种驱动把JDBC API映射到其他类型的数据库API 采用Java语言和JNI编写的驱动 采用一个中间的代理服务器,类似于阿里的中间件一样的东东 JDBC 直接跟数据库交互,最直接的方式
与其说oracle划分了四种类型,不如说oracle提供了四种方案,让我们可以选择。
Connection
对于数据库与业务之间,Connection是我们的主体部分,大部分异常处理其实都是这货给的。这里不得不说一句,Golang的多返回值是多么的好用呀,让异常都消失吧。在JDBC中获取链接的方式有两种,分别如下:
DriverManager: 相信众多初学者跟我一样,这个是我们第一个接触到的类,对于另一个我觉得可能大部分人应该不会太熟悉,这里就不再说他了。 DataSource: 其实这个接口是oracle提倡使用的接口,因为他提供了更多细节
1. 使用DriverManager
没什么好说的,我直接贴代码了~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | private static final String DB_USERNAME = "admin" ;
private static final String DB_PASSWORD = "admin" ;
public Connection getConnection() throws SQLException{
Connection conn = null ;
Properties connectionProps = new Properties();
connectionProps.put( "user" , DB_USERNAME);
connectionProps.put( "password" , DB_PASSWORD);
conn = DriverManager.getConnection(DB_URL, connectionProps);
return conn;
}
|
2.采用DataSource获取Connection
DataSource接口由各个驱动器厂商实现,oracle把他们划分如下:
基本的DataSource,它产生的每一个Connection都互不干扰,没有池以及分布式事务的概念 支持连接池的DataSource 支持分布式事务的DataSource
oracle 规定,一个驱动至少实现一个基本的DataSource,在学习数据库的时候我相信大家肯定见过BaseicDataSouce这个东西,当时我看到的时候我也不明白,不过现在明白了,哈哈哈,在使用DataSource中其实比DriverManager更复杂,但是更实.
2.1 部署Baseic DataSource
创建一个DataSource 类实例 设置属性 使用JNDI注册DataSource实例
创建数据源并且设置属性
1 2 3 4 | BasicDatabaseSource ds = BasicDatabaseSource();
ds.setServerName( "server" );
ds.setDatabaseName( "database" );
ds.setDescription( "description" );
|
使用JNDI
1 2 | Context ctx = new InitialContext();
ctx.bind( "jdbc/sty" , ds);
|
在学习之前,觉得JNDI到底是什么,其实花点时间,就知道了,道理很简单,反正Java EE容器做了。
获取数据源
1 2 3 4 | Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup( "jdbc/sty" );
Connection conn = ds.getConnection( "server" , "database" );
|
使用DatabaseSource 解决了数据库url的硬编码,当然有很多方式可以解除硬编码,除此之外,DatabaseSource一般会和ConnectionPoolDataSrouce配合食用。类似的,Datasource还和XADataSource一起使用,实现分布式事务。
2.2 部署Pooled DataSource
部署Pooled DataSource比Basic DataSource稍微复杂一些(分布式也是类似),不过都是模版话的配置,学习成本并不高。
之前提到过,Polled DataSource与基本的DataSource是配合使用的,意味着我们需要部署两个类。下面假设我们有两个类,第一个类是我们的池实现类:
1 2 3 | class PooledDataSource implements javax.sql.ConnectionPoolDataSource{
// implements here
}
|
第二个类如下:
1 2 3 | class FastDataSource implements javax.sql.DataSource{
// adapt ConnectionPoolDataSource here
}
|
为什么采用这样的方式,其实去看看JDBC的接口就可以了。
下面就是使用它们的时候了,首先我们注册我们的连接池类:
1 2 3 4 5 6 7 8 | PooledDataSource pds = new PooledDataSource();
pds.setServerName( "pds" );
pds.setDatabaseName( "database" );
pds.setPortNumber(3800);
pds.setDescription( "Polled DataSource" );
Context ctx = new InitialContext();
ctx.bind( "jdbc/pool/datasource" , pds);
|
现在设置我们的另一个类:
1 2 3 4 5 | FastDataSource fds = new FastDataSource();
fds.setDescription( "produces pooled connections to COFFEEBREAK" );
fds.setDataSourceName( "jdbc/pool/fastCoffeeDB" );
Context ctx = new InitialContext();
ctx.bind( "jdbc/fastDatasource" , fds);
|
使用数据源:
1 2 | ctx = new InitialContext();
ds = (DataSource)ctx.lookup( "jdbc/fastCoffeeDB" );
|
2.3 部署分布式事务 DataSource
与池比较类似,我们有两个类:
1 2 3 4 5 6 7 | class XATransactionalDataSource implements javax.sql.XADataSource{
// implements here
}
class TransactionalDataSource implements javax.sql.DataSource{
// adapt here
}
|
部署如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | XATransactionalDataSource xads = new XATransactionalDataSource();
xads.setServerName( "xads" );
xads.setDatabaseName( "database" );
xads.setPortNumber(9040);
xads.setDescription( "Distributed transactions DataSource" );
Context ctx = new InitialContext();
ctx.bind( "jdbc/xa/xads" , xads);
TransactionalDataSource tds = new TransactionalDataSource();
tds.setDescription( "suibianla" );
tds.setDataSourceName( "jdbc/xa/xads" );
Context ctx = new InitialContext();
ctx.bind( "jdbc/ds" , ds);
|
使用如下:
1 2 3 | Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup( "jdbc/ds" );
Connection con = ds.getConnection();
|
Statement
按照API的划分,Statement分为三大类:
Statement PreparedStatement CallableStatement
这部分比较简单,再次就不在介绍了,以后有时间的话再来完善。
ResultSet
ResultSet 按照功能被划分为三类,这里直接使用它们的定义介绍了:
TYPE_FORWARD_ONLY:最简单,功能最单一,类似于c++里面的强项迭代器 TYPE_SCROLL_INSENSITIVE:次之,类似于c++里面的双向迭代器 TYPE_SCROLL_SENSITIVE:有双向迭代器的功能,并且和底层数据库保持连接,如果的层数据库发生变化,他会更新内存
按照是否能修改的层数据库划分为两类:
CONCUR_READ_ONLY CONCUR_UPDATABLE
读取ResultSet的时候有有一个概念需要介绍,那就是Cursor,翻译成光标感觉不太好,下文就直接沿用Cursor这个单词,当调用Collection.commit的时候ResultSet对象会自动关闭,有时候我们的需求可能并不是这样,这时候我们需要设置另一个属性,ResultSet提供了这个属性,描述如下:
HOLD_CURSORS_OVER_COMMIT:ResultSet 的cursor不会被关闭。 CLOSE_CURSORS_AT_COMMIT:如果追求性能的话,建议采用这种方式。
Transactions
事务的主要功能是把数据库操作划分成一个单元,这个单元可以看作是原子的,其中事务也有不同的等级划分,这里直接摘抄一个表格:
事务隔离等级 |
是否会话 |
读取的脏数据 |
幂等读取 |
幻觉的读取 |
TRANSACTION_NONE |
不适用 |
不适用 |
不适用 |
不适用 |
TRANSACTION_READ_COMMITTED |
支持 |
不允许 |
允许 |
允许 |
TRANSACTION_READ_UNCOMMITTED |
支持 |
允许 |
允许 |
允许 |
TRANSACTION_REPEATABLE_READ |
支持 |
不允许 |
不允许 |
允许 |
TRANSACTION_SERIALIZABLE |
支持 |
不允许 |
不允许 |
不允许 |