JDBC
java datebase connectivity:是(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程。真正执行的代码是驱动Jar包中的实现类。
使用步骤
导入驱动Jar包
注册驱动
获取数据库连接对象
定义sql
获取执行sql语句的对象 Statement
执行sql,接受返回结果
处理结果
释放资源
简单示例
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3","root","root");
String sql = "update accout set balance =2000 where id =1";
Statement stmt =conn.createStatement();
int count =stmt.executeUpdate(sql);
System.out.println(count);
stmt.close();
conn.close();
对象解读
DriverManager
作用
注册驱动也叫驱动管理对象,告诉程序使用哪一个数据库驱动Jar
分析
通过查看源码发现,在com.mysql.jdbc.Driver类中存在静态代码块,可知使用了
registerDriver()
static{
try{
java.sql.DriverManager.registerDriver(new Driver());
}catch(SQLException E){
throw new RuntimeException("Can/t register driver");
}
}
方法
Statement createStatement() 创建一个Statement对象,用于将
SQL语句发送到数据库
static void registerDriver(Driver driver)
注册与给定的驱动程序
DriverManager
static Connection getConnetcion(String url ,String user ,
String password)
尝试建立与给定数据库URL的连接
Connection (接口)
作用
获取执行sql 的对象,与管理事务的开启、提交、回滚
方法
Statement createStatement() 创建一个Statement对象,用于将
SQL语句发送到数据库
PreparedStatement prepareStatement(String sql)
创建一个PreparedStatement对
象用于将参数化的SQL语句发送到数
据库
boolean getAutoCommit() 检索此Connection对象的当前自动
提交模式
void setAutoCommit(boolean autoCommit)
将此连接的自动提交模式设置为给
定状态
void rollback() 撤销当前事务中所做的所有更改,并
释放此Connection对象当前持有的
任何数据库锁
Statement (接口)
作用
执行sql的对象
方法
void addBatch(String sql) 把给定的sql命令添加到此
Statement对象的当前命令列表中
void clearBatch() 清空Statement对象当前sql命令
列表
int[] executeBatch() 将一批命令提交到数据库以执行,并
且所有命令都执行成功,返回一个
更新计数的数组
boolean execute(String sql) 执行给定的sql语句,这可能返回多
个结果
ResultSet executeQuery(String sql)
执行给定的sql语句,该语句返回单
个ResultSet对象
int executeUpdate(String sql) 执行的sql语句,可能
Insert,update或delete语句
ResultSet
作用
接受返回的结果集,封装查询结果
方法
String getString( int columnIndex )
检索的当前行中指定列的值,指定
行数
String getString ( int columnLabel )
检索的当前行中指定列的值,指定
列名
boolean next() 将光标从当前位置向前移动一行,
默认在字段名的列
PreparedStatement
须知
在拼接sql时,有些sql的特殊关键字与字符串拼接,会造成安全性问题,比如
输入用户,随意输入密码:a' or 'a' = 'a
最终拼接到sql中的样式是:select * from user where username='ferao' and
password ='a' or 'a' = 'a',这就叫做sql注入
作用
执行sql的对象,并且解决sql注入问题,使用PrepareStatement对象来解决,通过预编
译的sql方式,参数使用?占位符来代替
示例优化
Connection conn = null;
Statement stmt = null;
try{
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3","root","root");
String sql = "update accout set balance =2000 where id =1";
stmt =conn.createStatement();
int count =stmt.executeUpdate(sql);
System.out.println(count);
if(count > 0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
}catch(ClassNotFoundException e){
e.printStackTrace();
}catch(SQLExcention e){
e.printStackTrace();
}finally{
if(stmt != null){
try{
stmt.close();
}catch(SQLExcention e){
e.printStackTrace();
}
}
if(conn!= null){
try{
conn.close();
}catch(SQLExcention e){
e.printStackTrace();
}
}
}
批处理
场景
当需要向数据库发送一批sql语句来执行时,应避免向数据库一条条的发送执行,而是应该
采用jdbc的批处理机制,来提升执行效率
方式
方式一
如果有100条sql语句,就调用addBatch(sql)方法,它会把sql语句加到list集合
中
Statement.addBatch(sql) 素有sql加到list集合中
executeBatch() 执行批处理命令
clearBatch() 清理批处理命令
方式二
用PreparedStatement语句时只能向数据库中发送一种sql语句,只是可以做批处
理。而第一种方式可以发送不同的sql语句。实际开发中这种用的比较多。
批处理示例一
@Test
public void test1() {
Connection conn = null;
//用Statement去做
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql1 = "insert into testbatch(id,name) values('1','aaa')";
String sql2 = "update testbatch set name='bbb' where id='1'";
st = conn.createStatement();
st.addBatch(sql1);
st.addBatch(sql2);
//返回int[] 第一条sql影响数据库几行,第二条sql影响数据库几行
st.executeBatch();
//发给数据库之后,就要清理批处理
st.clearBatch();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.release(conn, st, rs);
}
}
批处理示例二
@Test
public void test2() {
Connection conn = null;
//用Statement去做
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql = "insert into testbatch(id,name) values(?,?)";
ps = conn.prepareStatement(sql);
/*
//1
ps.setString(1,"2");
ps.setString(2,"qwer");
ps.addBatch();
//2
ps.setString(1,"3");
ps.setString(2,"asdf");
ps.addBatch();
*/
//换用for循环实现
for (int i = 1; i <= 10; i++) {
ps.setString(1, i + "");
ps.setString(2, "aa" + i);
ps.addBatch();
}
//返回int[] 第一条sql影响数据库几行,第二条sql影响数据库几行
ps.executeBatch();
//发给数据库之后,就要清理批处理
ps.clearBatch();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.release(conn, ps, rs);
}
}
[注]
建议1000条做一个批处理,因为若数量较多,会报超出内存空间错误
批处理示例二(优化)
@Test
public void test2() {
Connection conn = null;
//用Statement去做
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql = "insert into testbatch(id,name) values(?,?)";
ps = conn.prepareStatement(sql);
//换用for循环实现
for (int i = 1; i <= 10; i++) {
ps.setString(1, i + "");
ps.setString(2, "aa" + i);
ps.addBatch();
if (i % 1000 == 0) {
//返回int[] 第一条sql影响数据库几行,第二条sql影响数据库几行
ps.executeBatch();
//发给数据库之后,就要清理批处理
ps.clearBatch();
}
}
ps.executeBatch();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.release(conn, ps, rs);
}
}
[注]
mysql插入1000万条数据大概要3个多小时,oracle就只需要6分多钟
JDBC 控制事务
事务
含义
一个包含多个步骤的业务操作,如果这个业务操作被事务管理,则这多个步骤要么同时成
功,要么同时失败
操作
1.开启事务 setAutoCommit(boolean autoCommit)
2.提交事务 commit()
3.回滚事务 rollback()
public class ConnectionTest {
public static void main (String[] args) {
Connection conn = null;
Statement stmt = null;
try{
//导入驱动Jar包
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取数据库连接对象
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3","root","root");
conn.setAutoCommit(false);
//定义sql
String sql = "update accout set balance =2000 where id =1";
//获取执行sql的对象 Statement
stmt =conn.createStatement();
//执行sql
int count =stmt.executeUpdate(sql);
//处理结果
System.out.println(count);
if(count > 0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
conn.commit();
}catch(Exception e){
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally{
if(stmt != null){
try{
if (conn != null){
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!= null){
try{
conn.close();
}catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
数据库连接池技术
技术起源之问题
由于创建连接和释放连接都有很大的开销,尤其是数据库服务器不在本地时,每次建立连接都需要TCP的三
次握手,且释放连接需要进行TCP四次握手,造成的开销是不可忽视的
技术起源之方案
为了提升系统访问数据库的性能,可以事先创建若干连接放置于连接池,需要时直接从连接池获取,使用结
束时归还给连接池而不必关闭连接,从而避免频繁创建和释放连接所造成的开销。聪明的人应该已经知道了
,这是典型的用空间换取时间的策略,即浪费了空间来存储连接,但节省了创建和释放连接的时间
技术起源之分析
数据库连接池技术在java开发中很常见。进一步拓展,在使用线程时创建线程连接池和此的道理是一样的。
基于JAVA的开源数据库连接池主要有:C3P0,DBCP,Druid等等。
在计算机系统中时间和空间是不可调和的矛盾,理解这一点对设计满足性能要求的算法是至关重要的。大
型网站性能优化的一个关键就是使用缓存,而缓存跟连接池道理非常类似,也是使用空间换取时间的策略
。可以将热点数据放置于缓存中,当用户查询这些数据时,可以直接从缓存中得到,这无论如何也快过,
去数据库中查询
须知
数据库连接池本质是DataSource接口,由各个数据库厂商去实现这套接口,提供数据库连接池驱动jar包
开发者可以使用这套接口编程,真正执行的代码是驱动Jar包中的实现类
含义
1. 数据库连接池是一个容器(集合),存放数据库连接的容器
2. 当系统初始化后,容器被创建,容器会申请一些连接对象,当用户来访问数据库时,从容器中获取连
接,用户访问完之后,会将连接对象还给容器
实现
1.获取连接,getConnection()
2.归还连接,如果连接对象Connection 是从连接池中获取的,那么调用 Connection.close()方
法,则不会关闭连接,而是归还连接,即对close()进行了增强
数据库连接池
C3P0 Druid(由阿里巴巴提供) Spring JDBC(spring封装Jdbc工具类 )
C3P0
操作
1.导入依赖(c3p0-o.9.5.2.jar,mchange-commons-java-0.2.12.jar,数据库驱动jar)
2.定义配置文件(名称是c3p0.properties或c3p0-config.xml),路径直接将文件放在
src目录下即可
3.创建核心对象(数据库连接对象 CombopooledDataSource)
4.获取连接,getConnection()
Druid
操作
1.导入依赖(druid-1.0.9.jar)
2.定义配置文件(properties形式,可以叫任意名称,可以放在任意目录下)
3.获取数据库连接池对象(通过工厂来获取 DruidDataSourceFactory)
4.获取连接,getConnection
public class ConnectionTest {
public static void main (String[] args)
{
Connection conn =null;
PreparedStatement ps =null;
try {
conn = JdbcUtils.getConnection();
String sql = "insert into emp values (?,?,?)" ;
ps = conn.prepareStatement(sql);
ps.setString(1,"2");
ps.setString(2,"张三");
ps.setString(3,"男");
int count = ps.executeUpdate();
System.out.println(count);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(ps,conn);
}
}
}
public class JdbcUtils {
private static DataSource ds ;
static{
//加载配置文件
Properties pro = new Properties();
try {
pro.load(JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
/**
* 获取连接池方法
*/
public static DataSource getDataSource (){
return ds ;
}
/**
* 释放资源
*/
public static void close(Statement stmt , Connection conn){
if (stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();//归还连接
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 释放资源
*/
public static void close(ResultSet rs , Statement stmt , Connection conn){
if (stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();//归还连接
} catch (SQLException e) {
e.printStackTrace();
}
}
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/bookstorage
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000
Spring JDBC
含义
spring 框架对jdbc的简单封装,提供了一个jdbcTemplate 对象简化jdbc的开发
操作
1.导入依赖
2.创建JdbcTemplate对象(依赖于数据源DataSource)
JdbcTemplate template = new JdbcTemplate (ds)
3.调用方法,JdbcTemplate的方法来完成CRUD的操作
1.update (): 执行DML语句。增删该 语句
2. queryForMap (): 查询结果将结果集封装为map集合
3. query() :查询结果,将结果封装为JavaBean对象