JDBC学习笔记
来源:互联网 发布:windows重置此电脑失败 编辑:程序博客网 时间:2024/05/29 04:54
JDBC简介
数据库驱动
数据库厂商为了方便开发人员从程序中操作数据库而提供的一套jar包,导入这个jar包就可以调用其中的方法操作数据库,这样的jar包就叫做数据库驱动。
JDBC
SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC。
JDBC全称为:Java DataBase Connectivity(java数据库连接),它主要由接口组成。
组成JDBC的2个包(均包含在J2SE里): java.sql
javax.sql
开发JDBC应用除了需要以上2个包的支持外,还需要导入相应JDBC的数据库实现(即数据库驱动)。
JDBC快速入门
首先利用cmd命令建立一个数据库,新建一个user表并写入三条数据。代码如下:
create table user( id int primary key auto_increment, name varchar(40), password varchar(40), email varchar(60), birthday date)character set utf8 collate utf8_general_ci;insert into user(name,password,email,birthday) values('zs','123456','zs@sina.com','1980-12-04');insert into user(name,password,email,birthday) values('lisi','123456','lisi@sina.com','1981-12-04');insert into user(name,password,email,birthday) values('wangwu','123456','wangwu@sina.com','1979-12-04');
建好数据库和表之后,再新建一个Java工程,工程建好后需要新建一个lib文件夹导入MySQL的驱动包mysql-connector-java-5.0.8-bin.jar
,并添加到BuildPath中。
准备工作完成以后,新建一个类JDBCDemo1.java
,用来实现对user表的查询。基本的代码如下:
package me.zipstream.jdbc;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import com.mysql.jdbc.Driver;public class JDBCDemo1 { public static void main(String[] args) throws SQLException { //1,注册数据库驱动 DriverManager.registerDriver(new Driver()); //2,获取数据库连接 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day10", "root", "root"); //3,获取传输器对象 Statement statement = conn.createStatement(); //4,利用传输器对象传输SQL语句到数据库中执行,获取结果集对象 ResultSet rs = statement.executeQuery("select * from user"); //5,遍历结果集获取查询结果 while (rs.next()){ String name = rs.getString("name"); System.out.println(name); } //6,关闭资源 rs.close(); statement.close(); conn.close(); }}
程序的细节和优化
DriverManager
JDBC程序中的DriverManager用于加载驱动,并创建与数据库的连接,这个API的常用方法有:
DriverManager.registerDriver(new Driver());DriverManager.getConnection(String url, String user, String password);
注意:在实际开发中并不推荐采用registerDriver方法注册驱动。原因有二:
1、查看Driver的源代码可以看到,如果采用此种方式,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象。
2、程序依赖mysql的api,脱离mysql的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。
推荐方式:
Class.forName(“com.mysql.jdbc.Driver”);
采用此种方式不会导致驱动对象在内存中重复出现,并且程序仅仅只需要一个字符串,不需要依赖具体的驱动,使程序的灵活性更高。
同样,在开发中也不建议采用具体的驱动类型指向getConnection方法返回的connection对象。正所谓面向接口编程,在导包的时候尽量不要导入与具体数据库有关的实现类,而是要导入接口。
附:Driver的源代码
package com.mysql.jdbc;import java.sql.SQLException;public class Driver extends NonRegisteringDriver implements java.sql.Driver { static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } public Driver() throws SQLException { // Required for Class.forName().newInstance() }}
数据库URL
URL用于标识数据库的位置,程序员通过URL地址告诉JDBC程序连接哪个数据库,URL的写法为: jdbc:mysql:[]//localhost:3306/test ?参数名:参数值
常用数据库URL地址的写法:
Oracle写法:jdbc:oracle:thin:@localhost:1521:sid
SqlServer写法:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid
MySql写法:jdbc:mysql://localhost:3306/sid
Mysql的url地址的简写形式: jdbc:mysql:///sid
url可以接的参数 user
、password
useUnicode=true&characterEncoding=UTF-8
Connection
JDBC程序中的Connection,代表数据库的连接,Connection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过Connection对象完成的,这个对象的常用方法有:
createStatement()
:创建向数据库发送sql的statement对象。prepareStatement(sql)
:创建向数据库发送预编译sql的PrepareSatement对象。prepareCall(sql)
:创建执行存储过程的callableStatement对象。setAutoCommit(boolean.autoCommit)
:设置事务是否自动提交。commit()
:在链接上提交事务。rollback()
:在此链接上回滚事务。
Statement
JDBC程序中的Statement对象用于向数据库发送SQL语句,Statement对象常用方法有:
- executeQuery(String.sql)
:用于向数据发送查询语句。
- executeUpdate(String.sql)
:用于向数据库发送insert、update或delete语句
- execute(String sql)
:用于向数据库发送任意sql语句
- addBatch(String.sql)
:把多条sql语句放到一个批处理中。
- executeBatch()
:向数据库发送一批sql语句执行。
ResultSet
JDBC程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式。ResultSet对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next()
方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。
ResultSet用于封装执行结果,所以该对象提供的都是用于获取数据的get方法:
获取任意类型的数据: getObject(int index)
getObject(string columnName)
其实以上两个方法主要是给框架设计者使用的。
获取指定类型的数据,例如: getString(int index)
getString(String columnName)
常用的数据类型转换表:
ResultSet还提供了对结果集进行滚动的方法(操作游标):
- next()
:移动到下一行
- Previous()
:移动到前一行
- absolute(int row)
:移动到指定行
- beforeFirst()
:移动resultSet的最前面。
- afterLast()
:移动到resultSet的最后面。
释放资源
JDBC程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象。
特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
为确保资源释放代码能运行,资源释放代码一定要放在finally语句中。
释放时后创建的先释放。
最终优化后的代码
package me.zipstream.jdbc;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class JDBCDemo1 { public static void main(String[] args) { Connection conn = null; Statement statement = null; ResultSet rs = null; try{ //1,注册数据库驱动 Class.forName("com.mysql.jdbc.Driver"); //2,获取数据库连接 conn = DriverManager.getConnection("jdbc:mysql:///day10?user=root&password=root"); //3,获取传输器对象 statement = conn.createStatement(); //4,利用传输器对象传输SQL语句到数据库中执行,获取结果集对象 rs = statement.executeQuery("select * from user"); //5,遍历结果集获取查询结果 while (rs.next()){ String name = rs.getString("name"); System.out.println(name); } } catch (Exception e) { e.printStackTrace(); } finally{ //6,关闭资源 if (rs != null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } finally{ rs = null; } } if (statement != null){ try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } finally{ statement = null; } } if (conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } finally{ conn = null; } } } }}
使用JDBC对数据库进行CRUD
准备工作
为了提高程序的复用性,减少书写重复代码,提高代码的灵活性。新建一个配置文件config.properties
:
driver=com.mysql.jdbc.Driverurl=jdbc:mysql:///day10user=rootpassword=root
再新建一个工具类JDBCUtils.java
,实现获取连接和关闭资源的功能。
package me.zipstream.utils;import java.io.FileReader;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Properties;public class JDBCUtils { private static Properties prop = null; private JDBCUtils(){} static{ prop = new Properties(); try { prop.load(new FileReader(JDBCUtils.class.getClassLoader().getResource("config.properties").getPath())); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 获取连接 * @return 对数据库的连接 * @throws ClassNotFoundException * @throws SQLException */ public static Connection getConn() throws ClassNotFoundException, SQLException{ Class.forName(prop.getProperty("driver")); return DriverManager.getConnection(prop.getProperty("url"), prop.getProperty("user"), prop.getProperty("password")); } /** * 关闭资源 * @param rs * @param st * @param conn */ public static void close(ResultSet rs, Statement st, Connection conn){ if (rs != null){ try { rs.close(); } catch (Exception e) { e.printStackTrace(); } finally{ rs = null; } } if (st != null){ try { st.close(); } catch (Exception e) { e.printStackTrace(); } finally{ st = null; } } if (conn != null){ try { conn.close(); } catch (Exception e) { e.printStackTrace(); } finally{ conn = null; } } }}
CRUD操作-create
想user表中增加一个记录。代码如下:
@Testpublic void add(){ Connection conn = null; Statement st = null; try { conn = JDBCUtils.getConn(); st = conn.createStatement(); int count = st.executeUpdate("insert into user values(null,'zhaoliu','123456','zhaoliu@qq.com','1999-09-09')"); if (count > 0){ System.out.println("操作成功!影响到"+ count +"行数据!"); } else{ System.out.println("操作失败!"); } } catch (Exception e) { e.printStackTrace(); } finally{ JDBCUtils.close(null, st, conn); }}
CRUD操作-updata
@Testpublic void update(){ Connection conn = null; Statement st = null; try{ conn = JDBCUtils.getConn(); st = conn.createStatement(); st.executeUpdate("update user set password=999 where name='zhaoliu'"); } catch (Exception e) { e.printStackTrace(); } finally{ JDBCUtils.close(null, st, conn); }}
CRUD操作-read
@Testpublic void find(){ Connection conn = null; Statement st = null; ResultSet rs = null; try{ conn = JDBCUtils.getConn(); st = conn.createStatement(); rs = st.executeQuery("select * from user where name='zhaoliu'"); while (rs.next()){ String name = rs.getString("name"); String password = rs.getString("password"); System.out.println(name + ": " + password); } } catch (Exception e) { e.printStackTrace(); } finally{ JDBCUtils.close(rs, st, conn); }}
CRUD操作-delete
@Testpublic void delete(){ Connection conn = null; Statement st = null; try{ conn = JDBCUtils.getConn(); st = conn.createStatement(); st.executeUpdate("delete from user where name='zhaoliu'"); } catch (Exception e) { e.printStackTrace(); } finally{ JDBCUtils.close(null, st, conn); }}
PreparedStatement
SQL注入攻击
SQL 注入是用户利用某些系统没有对输入数据进行充分的检查,从而进行恶意破坏的行为。由于dao中执行的SQL语句是拼接出来的,其中有一部分内容是由用户从客户端传入,所以当用户传入的数据中包含sql关键字时,就有可能通过这些关键字改变sql语句的语义,从而执行一些特殊的操作,这样的攻击方式就叫做sql注入攻击。典型的手段例如: username' or '1=1
username'#
就可以不输入密码而登录了。
PreparedStatement的使用
PreparedStatement
是Statement
的孩子,不同的是,PreparedStatement
使用预编译机制,在创建PreparedStatement
对象时就需要将sql语句传入,传入的过程中参数要用?
替代,这个过程回导致传入的sql被进行预编译,然后再调用PreparedStatement
的setXXX
将参数设置上去,由于sql语句已经经过了预编译,再传入特殊值也不会起作用了。从而有效防止SQL注入攻击。
代码样例:
package me.zipstream.jdbc;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import me.zipstream.utils.JDBCUtils;public class JDBCDemo3 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try{ conn = JDBCUtils.getConn(); ps = conn.prepareStatement("select * from user where name=? and password=?"); ps.setString(1, "zs"); ps.setString(2, "123456"); rs = ps.executeQuery(); while (rs.next()){ System.out.println(rs.getString("email")); } } catch (Exception e) { e.printStackTrace(); } finally{ JDBCUtils.close(rs, ps, conn); } }}
使用JDBC处理大数据
在实际开发中,程序需要把大文本Text
或二进制数据Blob
保存到数据库。
注:Text
是mysql叫法,Oracle中叫Clob
。
基本概念:大数据也称之为LOB(Large Objects),LOB又分为:
Clob和Blob Clob
用于存储大文本。 Blob
用于存储二进制数据,例如图像、声音、二进制文等。
对MySQL而言只有Blob
,而没有Clob
,mysql存储大文本采用的是Text
。 Text
分为: TINYTEXT
(255)、TEXT
(64k)、MEDIUMTEXT
(16M)和LONGTEXT
(4G)
Blob
分为: TINYBLOB
(255)、BLOB
(64k)、MEDIUMBLOB
(16M)和LONGBLOB
(4G)
使用JDBC处理大文本
首先在数据库中新建一个表,用来存储一个10.3M的文本数据。代码如下:
create table textdemo( id int primary key auto_increment, name varchar(100), content MEDIUMTEXT);
这里还需要修改执行程序虚拟机的最大和最小内存以及MySQL数据库存储文件的大小。在IDE的Run Dialog窗口的Argument选项卡中添加: -Xms64m
-Xmx256m
来修改虚拟机内存。
在MySQL的my.ini文件的[mysqld]
下添加: max_allowed_packet=64M
修改MySQL存储文件的大小。
大文本的存储
@Testpublic void addText(){ Connection conn = null; PreparedStatement ps = null; try{ conn = JDBCUtils.getConn(); ps = conn.prepareStatement("insert into textdemo values(null, ?, ?)"); ps.setString(1, "藏地密码"); File file = new File("1.txt"); ps.setCharacterStream(2, new FileReader(file), (int) file.length()); ps.executeUpdate(); } catch (Exception e){ e.printStackTrace(); } finally{ JDBCUtils.close(null, ps, conn); }}
大文本的查询
@Testpublic void findText(){ Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try{ conn = JDBCUtils.getConn(); ps = conn.prepareStatement("select * from textdemo where id=?"); ps.setInt(1, 1); rs = ps.executeQuery(); while (rs.next()){ String filename = rs.getString("name"); Reader reader = rs.getCharacterStream("content"); Writer writer = new FileWriter(filename); char[] cs = new char[1024]; int i = 0; while ((i=reader.read(cs))!=-1){ writer.write(cs, 0, i); } reader.close(); writer.close(); } } catch (Exception e){ e.printStackTrace(); } finally{ JDBCUtils.close(rs, ps, conn); }}
使用JDBC处理大二进制数据
方法与处理大文本并无两样。只要把setCharacterStream
和getCharacterStream
替换为setBinaryStream
和getBinaryStream
就可以了。
使用JDBC进行批处理
业务场景:当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。
Statement方式执行批处理
优点:可以执行多条不同结构的sql语句
缺点:没有使用预编译机制,效率低下,如果要执行多条结构相同仅仅参数不同的sql时,仍然需要写多次sql语句的主干
例子:一段代码完成创建数据库新建表和插入数据的操作
package me.zipstream.batch;import java.sql.Connection;import java.sql.Statement;import me.zipstream.utils.JDBCUtils;public class StatementBatch { public static void main(String[] args) { Connection conn = null; Statement st = null; try{ conn = JDBCUtils.getConn(); st = conn.createStatement(); st.addBatch("create database day10batch"); st.addBatch("use day10batch"); st.addBatch("create table batchDemo("+ "id int primary key auto_increment,"+ "name varchar(20)"+ ")"); st.addBatch("insert into batchDemo values(null,'aaaa')"); st.addBatch("insert into batchDemo values(null,'bbb')"); st.addBatch("insert into batchDemo values(null,'cc')"); st.addBatch("insert into batchDemo values(null,'d')"); st.executeBatch(); } catch (Exception e){ e.printStackTrace(); } finally{ JDBCUtils.close(null, st, conn); } }}
prparedStatement方式执行批处理
优点:有预编译机制,效率比较高.执行多条结构相同,参数不同的sql时,不需要重复写sql的主干
缺点:只能执行主干相同参数不同的sql,没有办法在一个批中加入结构不同的sql
例子:向psbatch表中存储10000条数据。
package me.zipstream.batch;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.Statement;import me.zipstream.utils.JDBCUtils;public class PreparedStatementBatch { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try{ conn = JDBCUtils.getConn(); ps = conn.prepareStatement("insert into psbatch values(null,?)"); for (int i=1; i<=10000; i++){ ps.setString(1, "name"+i); ps.addBatch(); if (i%1000 == 0){ ps.executeBatch(); ps.clearBatch(); } } ps.executeBatch(); } catch (Exception e){ e.printStackTrace(); } finally{ JDBCUtils.close(null, ps, conn); } }}
- JDBC学习笔记(二)
- JDBC学习笔记(二)
- JDBC学习笔记(三)
- JDBC学习笔记(四)
- JDBC学习笔记(六)
- JDBC学习笔记
- JDBC学习笔记
- JDBC学习笔记1
- jdbc学习笔记2
- jdbc学习笔记
- JDBC学习笔记
- jdbc学习笔记(1)
- jdbc学习笔记(2)
- jdbc学习笔记(3)
- JDBC学习笔记
- J2EE学习笔记--JDBC
- JDBC学习笔记
- JDBC学习笔记
- [LeetCode]Swap Nodes in Pairs
- [LeetCode]Divide Two Integers
- UI初级第一课 iPhone开发入门——iOS学习连载15
- jquery.artDialog.source.js学习
- H3C simware模拟器(解决不能tab/?)
- JDBC学习笔记
- 七个免费的Linux FTP客户端--转自51CTO
- 堆栈溢出攻击原理
- 天声人語 20150806
- 导航/画线
- DCC
- 有关一些sqlserver常用的错误
- 唱衰三星,国产手机厂商的狂妄与短视
- 2015/08/06