Java数据库操作(JDBC)
JDBC
Java数据库连接(Java DataBase Connectivity,JDBC)用于在Java程序中实现数据库操作功能,它提供了执行SQL语句、访问各种数据库的方法,并为各种不同的数据库提供统一的操作接口,java.sql包中包含了JDBC操作数据库的所有类。通过JDBC访问数据库一般有如下几个步骤:
(1)加载JDBC驱动器。将数据库的JDBC驱动加载到classpath中,在基于JavaEE的Web应用开发过程中,通常要把目标数据库产品的JDBC驱动复制到WEB-INF/lib下。
Oracle数据库导入的jar包为:ojdbc6.jar
mysql数据库导入的jar包为:mysql-connector-java-5.1.13-bin.jar
(2)加载JDBC驱动,并将其注册到DriverManager中。一般使用反射Class.forName(String driverName)。
(3)建立数据库连接,取得Connection对象。一般通过DriverManager.getConnection(url,username,password)方法实现,其中url表示连接数据库的字符串,username表示连接数据库的用户名,password表示连接数据库的密码。
(4)建立Statement对象或是PrepareStatement对象。
(5)执行SQL语句
(6)访问结果集ResultSet对象
(7)关闭资源。依次将ResultSet、Statement、PrepareStatement、Connection对象关闭,释放所占用资源。因为JDBC在底层通常都是通过网络IO实现SQL命令与数据传输的。
下面的例子分别测试了Oracle和mysql数据库的连接
程序目录结构
连接Oracle的操作OracleDb.java
package com.ghq.jdbc.oracle; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List; public class OracleDb {
private static String driver = "oracle.jdbc.driver.OracleDriver";
private static String url = "jdbc:oracle:thin:@localhost:1521:orcl";
private static String user = "scott";
private static String password = "itcast"; private static Connection conn;
private static PreparedStatement ps;
private static ResultSet rs; //加载驱动
static {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} //获得链接
public static Connection getConn(){
try {
conn = DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
} //建立PrepareStatement对象
public static PreparedStatement getPs(String sql,Object...args){
try {
getConn();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1, args[i]);
}
} catch (SQLException e) {
e.printStackTrace();
}
return ps;
} //查询的方法
public static ResultSet executeQuery(String sql,Object...args){
getPs(sql, args);
try {
rs = ps.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
return rs;
} //增删改的方法
public static int executeUpdate(String sql,Object...args){
getPs(sql, args);
int row = 0;
try {
row = ps.executeUpdate(); } catch (SQLException e) {
e.printStackTrace();
}
return row;
} //批处理的方法
public static int[] executeUpdateBathch(String sql,List<List> args){
getConn();
int rows[] = null;
try {
conn.setAutoCommit(false);
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.size(); i++) {
for (int j = 0; j < args.get(i).size(); j++) {
ps.setObject(i+1, args.get(i).get(j));
}
ps.addBatch();
}
rows = ps.executeBatch();
conn.commit();
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
return rows; } //关闭资源
public static void closeAll(){
try {
if (rs != null) {
rs.close();
} if (ps != null) {
ps.close();
} if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
连接mysql的操作MySqlDb.java
连接mysql和Oracle数据库的操作都是相同的,只需导入各自的jar,更改driver,url,user和password即可。
private static String driver = "com.mysql.jdbc.Driver";
private static String url = "jdbc:mysql://localhost:3306/test";
private static String user = "root";
private static String password = "root";
批处理
使用JDBC访问oracle数据库,做delete操作,因为oracle有自己的锁机制,即使多个线程同时删除,oracle仍然是一条一条删除,不同同时删除多项。
对于大量的数据更新,oracle有建议一些优化措施,就是使用批处理。
(1)首先通过调用setAutoCommit(false)方法来禁止自动提交,因为每删一条数据,oracle就要自动执行一次commit。commit是需要资源的,设置删除多条数据后再执行commit,可以节省资源。
(2)充分利用batch,如果不用batch,每个delete命令都需要从网络上传到oracle,若有1万条删除命令,要有1万次传送,如果将100个删除命令绑在一起送去Oracle执行。那就只要传送100次就可以了。大大缩短所需时间和网络资源。
setAutoCommit
setAutoCommit总的来说是保持数据的完整性循环里连续的进行插入操作,如果在开始时设置了:conn.setAutoCommit(false);,最后才进行conn.commit(),这样你即使插入的时候报错,修改的内容也不会提交到数据库,而如果没有手动的进行setAutoCommit(false);,出错时就会造成,前几条插入,后几条没有会形成脏数据。
setAutoCommit(false)的误用:(设定setAutoCommit(false)没有在catch中进行Connection的rollBack操作,操作的表就会被锁住,造成数据库死锁)
如果设置成非自动提交,在最后一定要调用commit或者rollback方法
测试连接的代码testconn.java
package com.ghq.textconn; import java.sql.ResultSet;
import java.sql.SQLException; import org.junit.Test; import com.ghq.jdbc.mysql.MySqlDb;
import com.ghq.jdbc.oracle.OracleDb; public class testconn {
//测试连接Oracle数据库,访问emp表编号为7369的员工信息
@Test
public void testOracle(){
String sql = "select * from emp where empno = ?";
ResultSet rs = OracleDb.executeQuery(sql,7369);
try {
while (rs.next()) {
System.out.println("Oracle连接 "+rs.getObject(1)+" "+rs.getObject(2)+" "+rs.getObject(3));
}
} catch (SQLException e) {
e.printStackTrace();
} } //测试连接mysql数据库,访问user表中id为1的用户信息
@Test
public void testmysql(){
String sql = "select * from user where id = ?";
ResultSet rs = MySqlDb.executeQuery(sql,1);
try {
while (rs.next()) {
System.out.println("mysql连接 "+rs.getObject(1)+" "+rs.getObject(2)+" "+rs.getObject(3));
}
} catch (SQLException e) {
e.printStackTrace();
} }
}
测试的结果为:
JDBC处理事务
一个事务是由一条或多条对数据库操作的SQL语句所组成的一个不可分割的工作单元,只有当事务中的所有操作都正常执行完了,整个事务才会提交给数据库。
在JDBC中一般是通过commit()方法和rollback()方法来结束事务的操作。commit()方法表示事务的提交,rollback()方法表示事务的回滚,多用于在处理事务的过程中出现了异常的情况,这两种方法位于java.sql.Connection类中。一般而言,事务默认操作时自动提交,即操作成功后,系统将自动调用commit方法,否则将调用rollback方法。
commit
平时使用commit和rollback主要在DML(数据操纵语言)insert,delete,update这些语句上,可以设置保存点,然后依次提交。单独commit,是把之前的数据改动都一次提交。正常的关闭,exit是自动提交的,可以不使用commit。直接关闭窗口,属于强制关闭,关闭之前需要commit。
事务具备四个特性(ACID):
(1)原子性(Atomicity)
指事务包含的所有的操作要么全部成功,要么全部失败回滚。(转账)
(2)一致性(Consistency)
指事务必须使数据库从一个一致状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。(转账两者的钱数总和是一致的)
(3)隔离性(Isolation)
指当多个用户并发访问数据库时,比如同时操作一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
(4)持久性(Durability)
指一个事务提交了,那么对数据库中的数据的改变就是永久性的。即便在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
事务的隔离性
不考虑事务的隔离性会发生的问题:
(1)脏读
脏读指在一个事务处理过程里读取了另一个未提交的事务中的数据。
当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。例如:用户A向用户B转账100元,对应SQL命令如下:
update account set money=money+100 where name=’B’; (此时A通知B)
update account set money=money - 100 where name=’A’;
当只执行第一条SQL时,A通知B查看账户,B发现确实钱已到账(此时即发生了脏读),而之后无论第二条SQL是否执行,只要该事务不提交,则所有操作都将回滚,那么当B以后再次查看账户时就会发现钱其实并没有转。
(2)不可重复读
不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发送了不可重复读。
不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
(3)幻读
幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
数据库提供的四种隔离级别:
① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
② Repeatable read (可重复读):可避免脏读、不可重复读的发生。
③ Read committed (读已提交):可避免脏读的发生。
④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。
级别越高,执行效率越低。Oracle默认为 read committed,MySql为Repeatable read。
Statement、PrepareStatement和CallableStatement区别
Statement用于执行不带参数的简单SQL语句,并返回它所生成结果的对象,每次执行SQL语句时,数据库都要编译该SQL语句。
Statement stmt = conn.getStatement();
stmt.executeUpdate(“insert into user values (2,’Allen’)”);
PreparedStatement表示预编译的SQL语句的对象,用于执行带参数的预编译SQL语句。
CallableStatement则提供了用来调用数据库中存储过程的接口,如果有输出参数要注册,说明是输出参数。
CallableStatement由prepareCall()方法所创建,它为所有DBMS提供了一种以标准形式调用已存储过程的方法。它从PrepareStatement中继承了用于处理输入参数的方法,而且还增加了调用数据库中的存储过程和函数以及设置输出类型参数的功能。
Statement和PrepareStatement对比
虽然Statement对象和PrepareStatement对象能够完成相同的功能,但相比之下,PrepareStatement具有以下的优点:
(1)效率更高。在使用PrepareStatement对象执行SQL命令时,命令会被数据库进行编译和解析,并放到命令缓冲区。然后每当执行同一个PrepareStatement对象时,由于在缓冲区中可以发现预编译的命令,虽然它还是会被再解析一次,但不会被再次编译,是可以重复使用的,能够有效提高系统性能。因此,如果要执行插入、更新、删除等操作,最好使用PrepareStatement来执行SQL语句。
(2)代码的可读性和可维护性更好。
使用Statement执行SQL语句
stmt.executeUpdate(“insert into user values ((2,’Allen’)”);
使用PrepareStatement执行SQL语句
String sql = “insert into user values (? ,?)”;
ps = con.prepareStatement(sql);
ps.setObject(1,2);
ps.setObject(2,”Allen”);
(3)安全性更好。使用PrepareStatement能够预防SQL注入攻击。
Sql注入指的是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器,达到执行恶意SQL命令的目的。注入只对SQL语句的编译过程有破坏作用,而执行阶段只是把输入字符串作为数据处理,不再需要对SQL语句进行解析,因此就避免了类似select * from user where name = ‘aa’ and psssword = ‘bb’ or 1=1的SQL注入问题的发生。
getString()和getObject()方法的区别
JDBC提供了getString()、getInt()和getData()等方法来从ResultSet中获取数据,当查询结果集中的数量量较小时,不用考虑性能,使用这些方法完全能够满足需求,但是当查询结果集中的数据量非常大时,则会抛出异常:OracleException未处理:ORA-01000:maximum open cursors exceeded。而通常情况下,使用getObject()方法就可以解决这个问题。
getString()或getInt()等方法在被调用的时候,程序会一次性地把数据都存放到内存中,然后通过调用ResultSet的next()和getString()等方法来获取数据。当数据量大到内存中放不下时就会抛出异常,而使用getObject()方法就不会出现这样的问题,因为数据不会一次性被读到内存中,每次调用时会直接从数据库中去获取数据。
参考博客
[1] 用JDBC访问ORACLE数据库 关于commit 增快效率 大数据 等的整理
https://www.cnblogs.com/qingxinblog/p/3375147.html
[2] Connection.setAutoCommit使用的注意事项
http://ygsilence.iteye.com/blog/1297762
Java数据库操作(JDBC)的更多相关文章
- 复习java数据库操作的总结
以前学习java数据库操作,学得那叫糊里糊涂,各种JDBC常用的类和接口根本是傻傻分不清啥是干嘛的.只是套着用用吧. 不过这次好歹清楚些了,呜呜,学习有阶段性,多次重复才有好效果,多么痛的领悟. 工程 ...
- java数据库操作:JDBC的操作
1,JDBC注意操作类及接口: 数据库操作过程: 1)打开数据库服务 2)连接数据库:一般都要输入用户名,密码, 3)操作数据库:创建表:查询表,更新,记录. 4)关闭数据库. 1,DriverMan ...
- Java数据库操作
一.JDBC 1.JDBC Java数据库连接,用于Java程序中实现数据库操作功能,java.sql包中提供了执行SQL语句,访问各种数据库的方法,并为各种不同的数据库提供统一的操作接口及类. 2. ...
- java程序员从笨鸟到菜鸟之(七)一—java数据库操作
本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.net/csh624366188 数据库访问几乎每一个稍微成型的程序都要用到的知识,怎么高效的访问数据库也是我们学习的一个 ...
- Java数据库编程(JDBC)
一.使用Java对数据库的操作步骤: 1.根据应用程序的数据库类型,加载相应的驱动: 2.连接到数据库,得到Connection对象: 3.通过Connection创建Statement对象: 4.使 ...
- 【转载】java数据库操作
数据库访问几乎每一个稍微成型的程序都要用到的知识,怎么高效的访问数据库也是我们学习的一个重点,今天的任务就是总结java访问数据库的方法和有关API,java访问数据库主要用的方法是JDBC,它是ja ...
- Java数据库操作(MySQL与SQLserver)
在java编程开发中,数据库的开发是重头戏. MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品: SQL Server是由Microsoft开发 ...
- Java 数据库操作
目录 Java数据库组织架构 下载驱动包 连接数据库 连接数据库的三个步骤 连接数据库的高开销 Statement接口介绍 PreparedStatement类 使用PreparedStatement ...
- JAVA数据库编程(JDBC技术)-入门笔记
本菜鸟才介入Java,我现在不急着去看那些基本的语法或者一些Java里面的版本的特征或者是一些晋级的知识,因为有一点.Net的OOP编程思想,所以对于Java的这些语法以及什么的在用到的时候在去发现学 ...
随机推荐
- java使用Redis5--分布式存储
Redis实现分布式存储的方法主要是采用一致性哈稀分片(Shard),将不同的key分配到不同的redis server上,达到横向扩展的目的. package redis; import java. ...
- mybatis常用默认配置
设置参数 描述 有效值 默认值 cacheEnable 该配置影响所有映射器中配置的缓存全局开关 true.false true lazyLoadingEnable 延迟加载的全局开关.当它开启时,所 ...
- multiprocess(上)
仔细说来,multiprocess不是一个模块而是python中一个操作.管理进程的包. 之所以叫multi是取自multiple的多功能的意思,在这个包中几乎包含了和进程有关的所有子模块.由于提供的 ...
- springMVC请求注解@RequestMapping各个属性值
最近遇到了一个采用fastJson传输数据的方式,搞了半天,总是感觉模糊,觉得自己有必要在这里做一个系统的总结,今天先从@RequestMapping的属性开始,采用REST 风格的 URL 请求,R ...
- javasript 的DOM 节点操作:创建,插入,删除,复制以及查找节点
DOM 含义: DOM 是文档对象模型(Document Object Model) 是一种基于浏览器编程的一套API 接口,我W3C 出台推荐的标准.其赋予了JS 操作节点的能力,当网页被加载时,浏 ...
- springcloud应用思考
1 springcloud注册中心eureka和zookeeper注册中心的区别: eureka注册中心,在服务选主的时候服务还是可以用的,zookeeper注册中心在选举的时候整个服务瘫痪了,是不可 ...
- Ubuntu 16.04防火墙
防火墙(ufw) 说明:简单版本的防火墙,底层依赖于iptables. 安装:sudo apt-get install ufw 查看状态:sudo ufw status 开启/关闭:sudo ufw ...
- header请求头信息详细介绍
https://www.byvoid.com/zhs/blog/http-keep-alive-header HTTP协议头部与Keep-Alive模式详解 1.什么是Keep-Alive模式? 我们 ...
- 祝福csdn回望2014,展望2015 大致可以这样总结和展望
祝福CSDN回望2014,展望2015 大致可以这样总结和展望 回望2014,大致可以这样总结: 从寻觅一架飞机开始,到寻觅另一架飞机结束,中间觅了几天金正恩. 全年都在寻觅收钱的人.送钱的人和带着钱 ...
- Python爬虫学习:Python内置的爬虫模块urllib库
urllib库 urllib库是Python中一个最基本的网络请求的库.它可以模拟浏览器的行为发送请求(都是这样),从而获取返回的数据 urllib.request 在Python3的urllib库当 ...