@File
2019-10-08T09:34:24.000000Z
字数 8334
阅读 143
java
理论
JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用java语言编写的类和接口组成。
JDBC制定了统一访问各类关系数据库的标准接口,为各个数据库厂商提供了标准接口的实现。
JDBC技术进行数据库操作主要有四种方式:
方式一:JDBC+ODBC桥接技术(100%不用)
微软有提供ODBC(Open DataBase Connectivity 开放数据库连接)技术,它可以连接任何数据库,可以通过JDBC间接访问ODBC来实现操作数据库的目的。
流程:程序->JDBC->ODBC->数据库,效果是最差的。
方式二:直接通过JDBC进行数据库操作
流程:程序->JDBC->数据库,性能是最好的。
方式三:通过网络连接技术来操作数据库
方式四:自己手动模拟通讯协议来操作数据库。
JDBC API支持两层和三层处理模型进行数据库访问,但在一般的JDBC体系结构由两层组成:
定义了一系列的接口和类,集成在java.sql和javax.sql包中。
JDBC API提供了以下接口和类:
DriverManager类:
这个类管理数据库驱动程序的列表。确定内容是否符合从Java应用程序使用的通信子协议正确的数据库驱动程序的连接请求。识别JDBC在一定子协议的第一个驱动器将被用来建立数据库连接。
Connection接口
此接口与接触数据库的所有方法。连接对象表示通信上下文,即,与数据库中的所有的通信是通过此唯一的连接对象。
Statement接口
可以使用这个接口创建的对象的SQL语句提交到数据库。一些派生的接口接受除执行存储过程的参数。
ResultSet接口
这些对象保存从数据库后,执行使用Statement
对象的SQL查询中检索数据。它作为一个迭代器,可以通过移动它来检索下一个数据。
String DRIVER_CLASS="com.mysql.cj.jdbc.Driver";//jdbc驱动类名称
Class.forName(DRIVER_CLASS);
//数据库url
String URL="jdbc:mysql://localhost:3306/test1?characterEncoding=utf8&useSSL=false&serverTimezone=CST&rewriteBatchedStatements=true";
String USER_NAME="root";// 用户名
String PASSWORD="123456";//密码
java.sql.Connection conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
注意:
在加载驱动及获取连接过程中,可能会出现异常,因此需要注意进行异常处理
DRIVER_CLASS:
- com.mysql.jdbc.Driver:mysql8以下使用
- com.mysql.cj.jdbc.Driver:mysql8使用
URL各参数描述:
- characterEncoding=utf8:设置编码方式
- useSSL=false:mysql8.0是不需要建立ssl连接的,你需要显示关闭
- serverTimezone=CST:serverTimezone是设置时区的,你需要设置CST,CST可视为美国、澳大利亚、古巴或中国的标准时间。
- rewriteBatchedStatements=true:mysql的批量操作需要设置此项为true
//通过Connection对象创建
java.sql.Statement sta - conn.createStatement();
这里分为二种:查询和更新(新增、修改、删除)
//执行sql查询返回ResultSet对象
String sql = "select * from a_student";
java.sql.ResultSet rs = sta.executeQuery(String sql);
while(rs.next()){
int id = rs.getInt("id");//按列名
//int id = rs.getInt(1);//按索引
String name = rs.getString("name");
//String name = rs.getString(2);
//...
}
//执行sql更新返回受影响的行数
int rows = sta.executeUpdate(String sql);
在使用JDBC与数据交互操作数据库中的数据后,应该明确地关闭所有的数据库资源以减少资源的浪费,对依赖于JVM的垃圾收集如下:
//注意:需从里到外的关闭
try{
if(rs!=null) rs.close();
if(sta!=null) sta.close();
if(conn!=null) conn.close();
} catch(Exception e){
e.printStackTrace();
}
需求:实现登录功能,需要通过传入的登录名和密码查询数据库表中的用户是否存在。
如果使用的是Statement语句来执行sql语句。
String sql="select * from admin where loginname='"+loginName+"' and loginpwd='"+loginPwd+"'";
如果应用中传入:
》请输入用户名:
test1
》请输入密码:
abc' or '1'='1
loginName="test1"
loginPwd="abc' or '1'='1"
那么当上面的sql拼接变量值之后的sql语句为
select * from admin where loginname='test1' and loginpwd='abc' or '1'='1'
那么将登录成功。
PreparedStatement
继承自Statement接口,对SQL进行预编译,从而提高数据库的执行效率。
PS:什么是预编译?
预编译语句PreparedStatement是java.sql中的一个接口,它是Statement的子接口。通过Statement对象执行sql语句时,需要将sql语句发送给DBMS,由DBMS首先进行编译再执行(在创建通道的时候并不进行sql的编译工作,事实上也无法进行编译)。而通过PreparedStatement不同,在创建PreparedStatement对象时就指定了sql语句,该语句立即发送给DBMS进行编译,当该语句被执行时,DBMS直接运行编译后的sql语句,而不需要像其他sql语句那样首先将被编译。
一般在考虑反复使用一个sql语句时才使用预编译,预编译语句常常放在一个循环中使用(在这种情况下预编译的优势就很明显了),通过反复设置参数从而达到多次使用该语句;
对SQL中的参数,允许使用占位符(占位符:?)的形式进行替换,简化SQL语句的编写,提高SQL语句执行效率,提高安全性。
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(dbUrl, user, password);
PreparedStatement pstmt = conn.prepareStatement("update content set introtext=? where id=?");
for(int i=0; i<10000; i++){
pstmt.setString(1, "abc"+i);
pstmt.setInt(2, id);
pstmt.executeUpdate();
}
这样,更新10000条数据,就得访问数据库10000次
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(dbUrl, user, password);
PreparedStatement pstmt = conn.prepareStatement("update content set introtext=? where id=?");
for(int i=0; i<10000; i++){
pstmt.setString(1, "abc"+i);
pstmt.setInt(2, id);
pstmt.addBatch();//添加到同一个批处理中
}
pstmt.executeBatch();//执行批处理
@Test
public void testPreparedStatementBatch(){
long starttime = System.currentTimeMillis();
ResultSet rs = null;
PreparedStatement sta = null;
String sql = "insert into a_student(stuNo,stuName,sex,age,create_time) values (?,?,?,?,?)";
try {
//1、加载驱动
Class.forName(DRIVER_CLASS);
//2、获得连接对象
Connection conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
//3、创建语句集
sta = conn.prepareStatement(sql);
for(int i=0;i<10008;i++){
sta.setInt(1, 10+i);
sta.setString(2, "aa"+i);
sta.setString(3, i%2==0?"男":"女");
sta.setInt(4, 20+i);
sta.setTimestamp(5, new java.sql.Timestamp(System.currentTimeMillis()));
sta.addBatch();
if(i%1000==0){
sta.executeBatch();
sta.clearBatch();
}
}
sta.executeBatch();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(rs!=null) rs.close();
if (sta != null) sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
long endtime = System.currentTimeMillis();
System.out.println("程序花费时间:" + (endtime-starttime)/1000 + "秒!!");
}
以.properties
为文件后缀,以键值(key/value)的格式保存内容
jdbc.mysql.driverClass=com.mysql.cj.jdbc.Driver
jdbc.mysql.url=jdbc:mysql://localhost:3306/test1?characterEncoding=utf8&useSSL=false&serverTimezone=CST&rewriteBatchedStatements=true
jdbc.mysql.username=root
jdbc.mysql.password=123456
package com.demo1;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* @author Ivy Li
* @create 2019-05-23
*/
public class DbConfig {
private static String dbConfigFile = "db.properties";
private static Properties props=null;
static {
props = new Properties();
InputStream is =null;
try {
is= DbConfig.class.getClassLoader().getResourceAsStream(dbConfigFile);
props.load(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(is!=null) is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 根据键获取值
* @param key 键
* @return
*/
public static String getValue(String key){
return props.getProperty(key);
}
}
String DRIVER_CLASS=DbConfig.getValue("jdbc.mysql.driverClass");
String URL=DbConfig.getValue("jdbc.mysql.url");
String USER_NAME=DbConfig.getValue("jdbc.mysql.username");
String PASSWORD=DbConfig.getValue("jdbc.mysql.password");
事务是一组原子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有指令。更简答的说就是:要么全部执行成功,要么撤销不执行。
比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!
@Test
public void testTransactionUpdate1(){
ResultSet rs = null;
PreparedStatement sta = null;
Connection conn = null;
try {
//1、加载驱动
Class.forName(DRIVER_CLASS);
//2、获得连接对象
conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
conn.setAutoCommit(false); //设置不自动提交事务
//3、创建语句集
String sql1 = "update a_student set age=age+10 where stuNo=1";
sta = conn.prepareStatement(sql1);
sta.executeUpdate();
String sql2 = "update a_student set age=age-10 where stuNo=2";
sta = conn.prepareStatement(sql2);
sta.executeUpdate();
conn.commit();//提交事务
System.out.println("success...");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(rs!=null) rs.close();
if (sta != null) sta.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@Test
public void testTransactionUpdate2(){
Connection conn = null;
ResultSet rs = null;
PreparedStatement sta = null;
Savepoint sp = null;
try {
//1、加载驱动
Class.forName(DRIVER_CLASS);
//2、获得连接对象
conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
conn.setAutoCommit(false); //设置不自动提交事务
//3、创建语句集
String sql1 = "update a_student set age=age+10 where stuNo=1";
sta = conn.prepareStatement(sql1);
sta.executeUpdate();
sp = conn.setSavepoint();//在这里设置事务回滚点
String sql2 = "update a_student set age=age-10 where stuNo=2";
sta = conn.prepareStatement(sql2);
sta.executeUpdate();
int a = 1/0;//报异常
String sql3 = "update a_student set age=age-10 where stuNo=3";
sta = conn.prepareStatement(sql3);
sta.executeUpdate();
conn.commit();//提交事务
System.out.println("success...");
} catch (Exception e) {
try {
conn.rollback(sp);//回滚到该事务点,即该点之前的会正常执行(sql1)
conn.commit();//回滚了要记得提交,如果没有提交sql1将会自动回滚
} catch (Exception e1){
e1.printStackTrace();
}
e.printStackTrace();
} finally {
try {
if(rs!=null) rs.close();
if (sta != null) sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
DAO模式是标准的J2EE设计模式之一.开发人员使用这个模式把底层的数据访问操作和上层的业务逻辑分开,此模式的主要作用是封装对数据库的各种操作;