java连接数据库

来源:互联网 发布:笔迹分析知乎 编辑:程序博客网 时间:2024/04/29 05:20

目录:
1.看一个例子:连接mysql数据库
2.相关类
3.关于数据库事务

PS:需要注意的是:在数据包涵中文字符时,可能会出现SQLExceptio(识别不了sql中的字符),这时需要检查数据库的字符集、表的字符集、连接的字符集是否支持中文。

1.看一个例子:连接mysql数据库

1.1使用JDBC连接Mysql数据库步骤:

1.1.1下载Mysql的jdbc驱动

将下载的压缩包解压缩,然后将里面的jar包导入到项目中(通过build path导入到项目lib库)
这里写图片描述

1.1.2

标准流程为:
一:加载驱动(加载并创建驱动实例)
二:调用DriverManager创建Connection实例
三:使用Connection,Statement,ResultSet操作数据库,获取结果集

1.1.3 实例:

import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class JDBC {        public static void main(String args[])         {                Connection conn = null;                String sql;                String url = "jdbc:mysql://localhost:3306/test?"                + "user=test&password=test&useUnicode=true&characterEncoding=UTF8";        try {            Class.forName("com.mysql.jdbc.Driver");// 动态加载mysql驱动,并创建实例            System.out.println("成功加载MySQL驱动程序");            // 一个Connection代表一个数据库连接            conn = DriverManager.getConnection(url);            // Statement里面带有很多方法,比如executeUpdate可以实现插入,更新和删除等            Statement stmt = conn.createStatement();            sql="CREATE TABLE if not exists `student`(`NO` int( 10 ) NOT NULL PRIMARY KEY AUTO_INCREMENT , `name` varchar( 20 ) CHARACTER SET utf8 COLLATE utf8_bin) CHARSET utf8 COLLATE utf8_bin";            int result = stmt.executeUpdate(sql);// executeUpdate语句会返回一个受影响的行数,如果返回-1就没有成功            if (result != -1)             {                System.out.println("创建数据表成功");                sql = "insert into student(name) values('陶伟基')";                result = stmt.executeUpdate(sql);                sql = "insert into student(name) values('周小俊')";                result = stmt.executeUpdate(sql);                sql = "select * from student";                ResultSet rs = stmt.executeQuery(sql);// executeQuery会返回结果的集合,否则返回空值                System.out.println("学号\t姓名");                while (rs.next())                 {                    System.out.println(rs.getString(1) + "\t" + rs.getString(2));// 入如果返回的是int类型可以用getInt()                }            }        } catch (SQLException e) {            System.out.println("MySQL操作错误");            e.printStackTrace();        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                                conn.close();                        } catch (SQLException e) {                                e.printStackTrace();                        }        }        }}

2.相关类

2.1 关于JDBC,Driver接口

这里所谓的驱动,其实就是实现了java.sql.Driver接口的类。如oracle的驱动类是 oracle.jdbc.driver.OracleDriver.class(此类可以在oracle提供的JDBC jar包中找到),此类实现了java.sql.Driver接口。

由于驱动本质上还是一个class,将驱动加载到内存和加载普通的class原理是一样的:使用Class.forName(“driverName”)。以下是将常用的数据库驱动加载到内存中的代码:
这里写图片描述

驱动加载入内存的过程


这里写图片描述

注意:Class.forName()将对应的驱动类加载到内存中,然后执行内存中的static静态代码段,代码段中,会创建一个驱动Driver的实例,放入DriverManager中,供DriverManager使用。

Driver的功能

java.sql.Driver接口规定了Driver应该具有以下功能:
这里写图片描述

acceptsURL(String url) 方法用来测试对指定的url,该驱动能否打开这个url连接。driver对自己能够连接的url会制定自己的协议,只有符合自己的协议形式的url才认为自己能够打开这个url,如果能够打开,返回true,反之,返回false;
connect(String url,Properties info)方法,创建Connection对象,用来和数据库的数据操作和交互,而Connection则是真正数据库操作的开始(在此方法中,没有规定是否要进行acceptsURL()进行校验)。

手动加载驱动 Driver 并实例化进行数据库操作的例子
这里写图片描述

上述的手动加载Driver并且获取连接的过程稍显笨拙:如果现在我们加载进来了多个驱动Driver,那么手动创建Driver实例,并根据URL进行创建连接就会显得代码杂乱无章,并且还容易出错,并且不方便管理。JDBC中提供了一个DriverManager角色,用来管理这些驱动Driver。

2.2DriverManager

DriverManager类:属于JDBC1.0方式,用来管理数据库驱动
这里写图片描述

DriverManager角色

事实上,一般我们操作Driver,获取Connection对象都是交给DriverManager统一管理的。DriverManger可以注册和删除加载的驱动程序,可以根据给定的url获取符合url协议的驱动Driver或者是建立Conenction连接,进行数据库交互。

这里写图片描述

DriverManager 内部持有这些注册进来的驱动 Driver,由于这些驱动都是 java.sql.Driver 类型,那么怎样才能获得指定厂商的驱动Driver呢?答案就在于:

java.sql.Driver接口规定了厂商实现该接口,并且定义自己的URL协议。厂商们实现的Driver接口通过acceptsURL(String url)来判断此url是否符合自己的协议,如果符合自己的协议,则可以使用本驱动进行数据库连接操作,查询驱动程序是否认为它可以打开到给定 URL 的连接。

使用DriverManager获取指定Driver

对于驱动加载后,如何获取指定的驱动程序呢?这里,DriverManager的静态方法getDriver(String url)可以通过传递给的URL,返回可以打开此URL连接的Driver。
比如,我想获取oracle的数据库驱动,只需要传递形如jdbc:oracle:thin:@::或者jdbc:oracle:thin:@//:/ServiceName的参数给DriverManager.getDriver(String url)即可:
Driver oracleDriver =DriverManager.getDriver(“jdbc:oracle:thin:@::”);
实际上,DriverManager.getDriver(String url)方法是根据传递过来的URL,遍历它维护的驱动Driver,依次调用驱动的Driver的acceptsURL(url),如果返回acceptsURL(url)返回true,则返回对应的Driver:
这里写图片描述

使用DriverManager创建 Connection 连接对象

创建 Connection 连接对象,可以使用驱动Driver的 connect(url,props),也可以使用 DriverManager 提供的getConnection()方法,此方法通过url自动匹配对应的驱动Driver实例,然后调用对应的connect方法返回Connection对象实例。

这里写图片描述

2.3 Statement

executing a static SQL statement and returning the results it produces

主要方法:

执行Batch相关的:

addBatch(String sql) :Adds the given SQL command to the current list of commands for this Statement object.clearBatch() ::executeBatch()  ::Submits a batch of commands to the database for execution and if all commands execute successfully, returns an array of update counts.

执行:

int executeQuery(String sql)::执行语句,但是不返回具体的结果executeQuery(String sql)  ::Executes the given SQL statement, which returns a single ResultSet objectboolean execute(String sql) ::Executes the given SQL statement, which may return multiple results.getConnection() ::获取当前Statement绑定的Connection

2.4CallableStatement:

对象为所有的 DBMS 提供了一种以标准形式调用已储存过程的方法。这种调用是用一种换码语法来写的,有两种形式:一种形式带结果参数,另一种形式不带结果参数(有关换码语法的信息,参见第 4 节“语句”)。结果参数是一种输出 (OUT) 参数,是储存过程的返回值。两种形式都可带有数量可变的输入(IN 参数)、输出(OUT 参数)或输入和输出(INOUT 参数)的参数。问号将用作参数的占位符。
在 JDBC 中调用已储存过程的语法如下所示。注意,方括号表示其间的内容是可选项;方括号本身并不是语法的组成部份。
返回结果参数的过程的语法为:

 {  ?= call <procedure-name>[(<arg1>,<arg2>, ...)]  }不带参数的已储存过程的语法:  {  call <procedure-name>[(<arg1>,<arg2>, ...)]    }

通常,创建 CallableStatement 对象的人应当知道所用的 DBMS 是支持已储存过程的,并且知道这些过程都是些什么。然而,如果需要检查,多种 DatabaseMetaData 方法都可以提供这样的信息。例如,如果 DBMS 支持已储存过程的调用,则 supportsStoredProcedures 方法将返回 true,而 getProcedures 方法将返回对已储存过程的描述。

2.4.1 创建 CallableStatement 对象

CallableStatement 对象是用Connection 方法 prepareCall 创建的。下例创建 CallableStatement 的实例,其中含有对已储存过程 getTestData 调用。该过程有两个变量,但不含结果参数:

CallableStatement cstmt = con.prepareCall( "{call getTestData(?, ?)}"); 

其中 ? 占位符为 IN、 OUT 还是 INOUT 参数,取决于已储存过程 getTestData。

2.4.2 IN 和 OUT 参数

将 IN 参数传给 CallableStatement 对象是通过 setXXX 方法完成的。该方法继承自 PreparedStatement。所传入参数的类型决定了所用的 setXXX 方法(例如,用 setFloat 来传入 float 值等)。

如果已储存过程返回 OUT 参数,则在执行 CallableStatement 对象以前必须先注册每个 OUT 参数的 JDBC 类型(这是必需的,因为某些 DBMS 要求 JDBC 类型)。注册 JDBC 类型是用 registerOutParameter 方法来完成的。语句执行完后,CallableStatement 的 getXXX 方法将取回参数值。正确的 getXXX 方法是为各参数所注册的 JDBC 类型所对应的 Java 类型。换言之, registerOutParameter 使用的是 JDBC 类型(因此它与数据库返回的 JDBC 类型匹配),而 getXXX 将之转换为 Java 类型。

作为示例,下述代码先注册 OUT 参数,执行由 cstmt 所调用的已储存过程,然后检索在 OUT 参数中返回的值。方法 getByte 从第一个 OUT 参数中取出一个 Java 字节,而 getBigDecimal 从第二个 OUT 参数中取出一个 BigDecimal 对象(小数点后面带三位数):

CallableStatement cstmt = con.prepareCall( "{call getTestData(?, ?)}"); cstmt.registerOutParameter(1, java.sql.Types.TINYINT); cstmt.registerOutParameter(2, java.sql.Types.DECIMAL, 3); cstmt.executeQuery(); byte x = cstmt.getByte(1); java.math.BigDecimal n = cstmt.getBigDecimal(2, 3); CallableStatement 与 ResultSet 不同,它不提供用增量方式检索大 OUT 值的特殊机制。 

2.4. 3 INOUT 参数

既支持输入又接受输出的参数(INOUT 参数)除了调用 registerOutParameter 方法外,还要求调用适当的 setXXX 方法(该方法是从 PreparedStatement 继承来的)。setXXX 方法将参数值设置为输入参数,而 registerOutParameter 方法将它的 JDBC 类型注册为输出参数。setXXX 方法提供一个 Java 值,而驱动程序先把这个值转换为 JDBC 值,然后将它送到数据库中。

这种 IN 值的 JDBC 类型和提供给 registerOutParameter 方法的 JDBC 类型应该相同。然后,要检索输出值,就要用对应的 getXXX 方法。例如,Java 类型为 byte 的参数应该使用方法 setByte 来赋输入值。应该给 registerOutParameter 提供类型为 TINYINT 的 JDBC 类型,同时应使用 getByte 来检索输出值 (第 8 节“JDBC 和 Java 类型之间的映射”将给出详细信息和类型映射表)。

下例假设有一个已储存过程 reviseTotal,其唯一参数是 INOUT 参数。方法 setByte 把此参数设为 25,驱动程序将把它作为 JDBC TINYINT 类型送到数据库中。接着,registerOutParameter 将该参数注册为 JDBC TINYINT。执行完该已储存过程后,将返回一个新的 JDBC TINYINT 值。方法 getByte 将把这个新值作为 Java byte 类型检索。

CallableStatement cstmt = con.prepareCall( "{call reviseTotal(?)}"); cstmt.setByte(1, 25); cstmt.registerOutParameter(1, java.sql.Types.TINYINT); cstmt.executeUpdate(); byte x = cstmt.getByte(1); 

2.4. 4 先检索结果,再检索 OUT 参数

由于某些 DBMS 的限制,为了实现最大的可移植性,建议先检索由执行 CallableStatement 对象所产生的结果,然后再用 CallableStatement.getXXX 方法来检索 OUT 参数。

如果 CallableStatement 对象返回多个 ResultSet 对象(通过调用 execute 方法),在检索 OUT 参数前应先检索所有的结果。这种情况下,为确保对所有的结果都进行了访问,必须对 Statement 方法 getResultSet、getUpdateCount 和 getMoreResults 进行调用,直到不再有结果为止。

检索完所有的结果后,就可用 CallableStatement.getXXX 方法来检索 OUT 参数中的值。

检索作为 OUT 参数的 NULL 值
返回到 OUT 参数中的值可能会是 JDBC NULL。当出现这种情形时,将对 JDBC NULL 值进行转换以使 getXXX 方法所返回的值为 null、0 或 false,这取决于 getXXX 方法类型。对于 ResultSet 对象,要知道 0 或 false 是否源于 JDBC NULL 的唯一方法,是用方法 wasNull 进行检测。如果 getXXX 方法读取的最后一个值是 JDBC NULL,则该方法返回 true,否则返回 flase。

2.5 Connection接口

代表和特定数据库的一个回话(在JDBC1.0中,可由DriverManager.getConnection()方法获得),SQL语句的执行、结果集的查询获取都是在Connection的上下文中得到的。
Conection能够提供它所关联的数据库的一些基本信息,如关于表的描述、所支持的SQL语法,存储过程,本连接的能力和一些其他信息,这些都可以通过Connection.getMetaData()方法获得。
注意:当需要配置Connection的属性的时候,应该直接使用Connection的方法来配置属性,如Connection.setAutoCommit,Connection.setTransactionIsolation,而不是直接通过提交sql语句的方式来改变(如果connection有相应的方法的话)。默认模式下,connection对象是auto-commit的。

2.5.1 关于auto-commit模式

在执行了SQL语句后,自动提交改变,来改变数据库状态。如果不是auto-commit模式,则必须在执行了SQL语句后,显示的调用commit方法,来让数据库同步状态,否则,这些sql语句影响不会保存。

2.5.2 主要方法:

commit() :Makes all changes made since the previous commit/rollback permanent and releases any database locks currently held by this Connection object.
createStatement() :Creates a Statement object for sending SQL statements to the database.
prepareCall(String sql) ::创建一个CallableStatement 对象,来调用存储过程
prepareCall(String sql, int resultSetType, int resultSetConcurrency) ::重载版本
prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) ::重载版本
prepareStatement(String sql) ::创建一个参数化的Statement对象
prepareStatement(String sql, int autoGeneratedKeys) ::
prepareStatement(String sql, int[] columnIndexes) ::Creates a default PreparedStatement object capable of returning the auto-generated keys designated by the given array.
prepareStatement(。。。)::其他重载版本见jdk

getAutoCommit() :查看当前连接是否是auto-commit模式的。
getMetaData() :Retrieves a DatabaseMetaData object that contains metadata about the database to which this Connection object represents a connection.
getTransactionIsolation() :获取当前连接的事务隔离级别
isClosed() :判断当前连接是否关闭
getHoldability() :Retrieves the current holdability of ResultSet objects created using this Connection object.(关于ResultSet的保持性:是关于事务提交后, 是否需要关闭结果集的问题,通过setHoldability()可设置:
HOLD_CURSORS_OVER_COMMIT: 当调用 Connection.commit()后, ResultSet 将不会关闭, 一般用于只读的结果集中.
CLOSE_CURSORS_AT_COMMIT : 当调用 Connection.commit()后, ResultSet 将会关闭.

2.5 PreparedStatement

2.5 PreparedStatement

代表一个预编译的SQL语句,通过Connection.prepareStatement(String)获得,可通过Setter方法设置In类型的参数。
PS:关于Setter方法的类型(如setShort,setString),它所指明的类型要和对应的SQL语句中的声明的类型相符、兼容,例如:Sql语句中参数是INteger的,则需要使用setInt方法。
例子:

PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES SET SALARY = ? WHERE ID = ?");   pstmt.setBigDecimal(1, 153833.00)   pstmt.setInt(2, 110592)

主要方法:

executeQuery() :
:Executes the SQL query in this PreparedStatement object and returns the ResultSet object generated by the query.

execute() ::

setByte(int parameterIndex, byte x) ::parameterIndex代表第几个占位符值,从1开始。
setXX:XX有Bytes,Boolean,Blob,Date,Float,Double,Int等。

2.6 ResultSet 接口

ResultSet 接口提供对数据表的访问。ResultSet 对象通常是通过执行“语句”来生成的。
ResultSet 始终有一个游标指向其当前数据行。最初,游标定位在第一行的前面。next() 方法将游标移至下一行。
getXXX 方法会检索当前行的列值。可使用列的索引号或列的名称来检索这些值。通常,使用列索引将更为有效。列是从 1 开始编号的。
结果集(ResultSet)是数据中查询结果返回的一种对象,可以说结果集是一个存储查询结果的对象,但是结果集并不仅仅具有存储的功能,他同时还具有操纵数据的功能,可能完成对数据的更新等。

结果集读取数据的方法主要是getXXX(),他的参数可以使整型表示第几列(是从1开始的),还可以是列名。返回的是对应的XXX类型的值。如果对应那列时空值,XXX是对象的话返回XXX型的空值,如果XXX是数字类型,如Float等则返回0,boolean返回false。使用getString()可以返回所有的列的值,不过返回的都是字符串类型的。XXX可以代表的类型有:基本的数据类型如整型(int),布尔型(Boolean),浮点型(Float,Double)等,比特型(byte),还包括一些特殊的类型,如:日期类型(java.sql.Date),时间类型(java.sql.Time),时间戳类型(java.sql.Timestamp),大数型(BigDecimal和BigInteger等)等。还可以使用getArray(int colindex/String columnname),通过这个方法获得当前行中,colindex所在列的元素组成的对象的数组。使用getAsciiStream(

结果集从其使用的特点上可以分为四类,这四类的结果集的所具备的特点都是和Statement语句的创建有关,因为结果集是通过Statement语句执行后产生的,所以可以说,结果集具备何种特点,完全决定于Statement,当然我是说下面要将的四个特点,在Statement创建时包括三种类型。首先是无参数类型的,他对应的就是下面要介绍的基本的ResultSet对应的Statement。下面的代码中用到的Connection并没有对其初始化,变量conn代表的就是Connection对应的对象。SqlStr代表的是响应的SQL语句。

2.6.1、最基本的ResultSet(只能往后)

之所以说是最基本的ResultSet是因为,这个ResultSet他起到的作用就是完成了查询结果的存储功能,而且只能读去一次,不能够来回的滚动读取。这种结果集的创建方式如下:
Statement st = conn.CreateStatement()
ResultSet rs = Statement.excuteQuery(sqlStr)
由于这种结果集不支持,滚动的读去功能所以,如果获得这样一个结果集,只能使用它里面的next()方法,逐个的读去数据。

2.6. 2、可滚动的ResultSet类型。

这个类型支持前后滚动取得纪录next()、previous(),回到第一行first(),同时还支持要去的ResultSet中的第几行absolute(int n),以及移动到相对当前行的第几行relative(int n),要实现这样的ResultSet在创建Statement时用如下的方法。

Statement st = conn.createStatement(int resultSetType, int resultSetConcurrency)ResultSet rs = st.executeQuery(sqlStr)其中两个参数的意义是:resultSetType是设置ResultSet对象的类型可滚动,或者是不可滚动。取值如下:       ResultSet.TYPE_FORWARD_ONLY只能向前滚动       ResultSet.TYPE_SCROLL_INSENSITIVE       Result.TYPE_SCROLL_SENSITIVE这两个方法都能够实现任意的前后滚动,使用各种移动的ResultSet指针的方法。二者的区别在于前者对于修改不敏感,而后者对于修改敏感。resultSetConcurency是设置ResultSet对象能够修改的,取值如下:       ResultSet.CONCUR_READ_ONLY 设置为只读类型的参数。       ResultSet.CONCUR_UPDATABLE 设置为可修改类型的参数。 所以如果只是想要可以滚动的类型的Result只要把Statement如下赋值就行了。Statement st = conn.createStatement(Result.TYPE_SCROLL_INSENITIVE,                           ResultSet.CONCUR_READ_ONLY); ResultSet rs = st.excuteQuery(sqlStr);用这个Statement执行的查询语句得到的就是可滚动的ResultSet。

2.6.3、可更新的ResultSet

这样的ResultSet对象可以完成对数据库中表的修改,但是我知道ResultSet只是相当于数据库中表的视图,所以并不时所有的ResultSet只要设置了可更新就能够完成更新的,能够完成更新的ResultSet的SQL语句必须要具备如下的属性:
a、只引用了单个表。
b、不含有join或者group by子句。
c、那些列中要包含主关键字。
具有上述条件的,可更新的ResultSet可以完成对数据的修改,可更新的结果集的创建方法是:
Statement st = createstatement(Result.TYPE_SCROLL_INSENSITIVE,Result.CONCUR_UPDATABLE)
这样的Statement的执行结果得到的就是可更新的结果集。更新的方法是,把ResultSet的游标移动到你要更新的行,然后调用updateXXX(),这个方法XXX的含义和getXXX()是相同的。updateXXX()方法,有两个参数,第一个是要更新的列,可以是列名或者序号。第二个是要更新的数据,这个数据类型要和XXX相同。每完成对一行的update要调用updateRow()完成对数据库的写入,而且是在ResultSet的游标没有离开该修改行之前,否则修改将不会被提交。
使用updateXXX方法还可以完成插入操作。但是首先要介绍两个方法:
moveToInsertRow()是把ResultSet移动到插入行,这个插入行是表中特殊的一行,不需要指定具体那一行,只要调用这个方法系统会自动移动到那一行的。
moveToCurrentRow()这是把ResultSet移动到记忆中的某个行,通常当前行。如果没有使用insert操作,这个方法没有什么效果,如果使用了insert操作,这个方法用于返回到insert操作之前的那一行,离开插入行,当然也可以通过next(),previous()等方法离开插入行。
要完成对数据库的插入,首先调用moveToInsertRow()移动到插入行,然后调用updateXXX的方法完成对,各列数据的更新,完成更新后和更新操作一样,要写到数据库,不过这里使用的是insertRow(),也要保证在该方法执行之前ResultSet没有离开插入列,否则插入不被执行,并且对插入行的更新将丢失。

2.6.4、可保持的ResultSet

正常情况下如果使用Statement执行完一个查询,又去执行另一个查询时这时候第一个查询的结果集就会被关闭,也就是说,所有的Statement的查询对应的结果集是一个,如果调用Connection的commit()方法也会关闭结果集。可保持性就是指当ResultSet的结果被提交时,是被关闭还是不被关闭。JDBC2.0和1.0提供的都是提交后ResultSet就会被关闭。不过在JDBC3.0中,我们可以设置ResultSet是否关闭。要完成这样的ResultSet的对象的创建,要使用的Statement的创建要具有三个参数,这个Statement的创建方式也就是,我所说的Statement的第三种创建方式。如下:
Statement st=createStatement(int resultsetscrollable,int resultsetupdateable,int resultsetSetHoldability)
ResultSet rs = st.excuteQuery(sqlStr);

前两个参数和两个参数的createStatement方法中的参数是完全相同的,这里只介绍第三个参数:
resultSetHoldability表示在结果集提交后结果集是否打开,取值有两个:
ResultSet.HOLD_CURSORS_OVER_COMMIT:表示修改提交时,不关闭数据库。
ResultSet.CLOSE_CURSORS_AT_COMMIT:表示修改提交时ResultSet关闭。

不过这种功能只是在JDBC3.0的驱动下才能成立。

2.7 DataSource接口

位于javax.sql包中
DataSource定义:作为产生特定数据源的Connection的工厂。在获取Connection上,相比于DriverManager,DataSource是一种更好的方案。实现DataSource接口的对象通常通过JNDI API来注册。

DataSource接口是由driver提供商实现. 不同的实现有:
1.基本实现Basic implementation – 产生标准的连接对象
2.连接池实现 – produces a Connection object that will automatically participate in connection pooling. This implementation works with a middle-tier connection pooling manager.
3.Distributed transaction implementation – produces a Connection object that may be used for distributed transactions and almost always participates in connection pooling. This implementation works with a middle-tier transaction manager and almost always with a connection pooling manager.

A DataSource object has properties that can be modified when necessary. For example, if the data source is moved to a different server, the property for the server can be changed. The benefit is that because the data source’s properties can be changed, any code accessing that data source does not need to be changed.
和DriverManager不同,一个DataSource对象能够识别和描述它所代表的数据源的属性,而且DataSource对象的工作和JNDI(Javatm Naming and Directory Interfaceti)具有密切的关系,DataSource的建立、发布、独立于应用程序的管理都依靠JNDI技术。
A driver that is accessed via a DataSource object does not register itself with the DriverManager. Rather, a DataSource object is retrieved though a lookup operation and then used to create a Connection object. With a basic implementation, the connection obtained through a DataSource object is identical to a connection obtained through the DriverManager facility.
在JDBC2.0或JDBC3.0中,所有的数据库驱动程序提供商必须提供一个实现了DataSource接口的类,要使用数据源必须首先在JNDI中注册该数据源对象。
如果在JNDI中注册了数据源对象,将会比起使用DriverManager来具有两个方面的优势:
首先程序不需要像使用DriverManager一样对加载的数据库驱动程序信息进行硬编码,程序员可以选择先在JNDI中注册这个数据源对象,然后在程序中使用一个逻辑名称来引用它,JNDI会自动根据你给出的名称找到与这个名称绑定的DataSource对象。然后就可以使用这个DataSource对象来建立和具体数据库的连接了。

An implementation of DataSource must include a public no-arg constructor.

2.7. 主要方法

这里写图片描述

其他

各个对象之间的关系说明

DriverManager::管理驱动,常用的方法是getConnection(),registerDriver()等,驱动管理,从特定驱动中获取一个回话Connection.(DriverManager的优点就是用户不用理会具体驱动的细节,只需要将特定数据库的驱动jar包导入到项目中,DriverManager就能够帮你找你要的驱动)

Connection::对特定的数据库的一个会话,可通过它获取数据库的信息,设置连接属性,同时可通过Connection获取下级对象Statement.

Statement::用来执行SQL语句,并返回结果的对象。可分为简单的Statement(通过connection.createStatement()创建,PreparedStatement,CallbableStatement)

ResultSet::一般通过statement.executeQuery(String sql)获取,获取某次执行的结果集。

3.关于数据库事务

3.1事物的ACID属性:

A(Atomicity)原子性:

在一个事务上下文里面,对数据库进行的任何操作,必须保证是原子的,也就是说要么不做,要么全部都做,不能只做一部分。比如insert一条数据和delete一条数据,不知能只做insert操作而不做delete操作

C(Consistency)一致性:

在事务的处理过程中,数据库必须时刻要避免被置于不一致 (inconsistent)的状态。这意味着在事务期间,每次对数据库实施的插入、更新或删除操作 时,数据库的完整性约束(integrity constraints)都要得到保证,即使在事务还未被提交时 也必须如此。比如非空约束。

I(Isolation)隔离性:

两个不同的事务相互之间是彼此隔离程度。有一种说法是事务之间是彼此隔离的,一个事务不能够读取另一个事务未提交的数据,这个不太准确,这个属于事务的隔离级别。wiki上的解释是“
The isolation property ensures that the concurrent execution of transactions results in a system state that could have been obtained if transactions are executed serially, i.e. one after the other“

D(Durability)持久性:

事务一旦提交,在事务中对数据库进行的修改也就进行持久化存储了,不会由于系统故障导致提交后的数据丢失。

3.2 SQL标准定义了4类隔离级别

包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

Read Uncommitted(读取未提交内容)

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read),如将MySQL调整到 这个级别,hibernate只要执行flush数据就进入了数据库,但是此时事务并没有提及(commit)。
这个是最弱的隔离级别,不满足ACID中的隔离性的要求,大多数数据库并不提供这个支持。见下面的例子
这里写图片描述

Read Committed(读取提交内容)

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别也支持所谓的不可重复读(Nonrepeatable Read),即读取的数据出现不一致,因为同一事务的其他实例处理其间可能会有新的commit,如两个相同的select语句间加入了一个add语句,会导致查询的结果出现不一致,所以同一select语句有可能返回不同结果。
不会出现脏读,保证事务B读取的都是事务A update提交之后的数据。但是对于insert操作,就可能存在脏读的问题,例如下面一个例子
这里写图片描述

#

Repeatable Read(可重读)

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发(指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。)读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行(即并发的事务,另一个事务提交了事务,影响了当前事务)。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题???什么东西,没看懂。
但是事务之间还是会存在互相影响的情况,见下面的例子
这里写图片描述
事务A和事务B都是先读取Price的价格,然后在价格上面减去一定的数值,我们期望结果是70,但是实际结果可能是90,也可能是80。

Serializable(可串行化)

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

0 0
原创粉丝点击