Java和数据库连接的方法
·····Native API (不能跨平台)
·····ODBC/JDBC-ODBC (效率差,无法跨平台)
·····JDBC (主流) Java Database Connectivity
Java SQL操作类库
java.sql.* (接口类)
javax.sql* (接口类)
根据数据库版本和JDBC含本合理选择jar包
连接字符串
-jdbc:oracle:thin:@127.0.0.1(IP可修改):1521(端口可改):dbname(数据库名可改)
-jdbc:mysql://localhost(IP可修改):3306(端口可改)/mydb(数据库名可改)
-jdbc:sqlserver://localhost:1433:DatabaseName=dbname
Java连接数据库操作步骤
构建连接(搭桥)
- 注册驱动,寻找材质(哪种数据库),class.forName("…");
- 确定对岸目标,建桥Connection
执行操作(派个人过桥,提个篮子,去拿数据)
- Statement (执行者)
- ResultSet(结果集)
释放连接(拆桥) connection.close()
Statement 执行者类
- 使用executeQuery()执行select语句,返回结果(二维数组)放在ResultSet
- 使用executeUpdate()执行insert/update/delete,返回修改的行数
- 一个Statement对象一次只能执行一个命令
ResultSet 结果对象
- next()判断是否还有下一条记录
- getInt/getString/getDouble/…获取单元格数据
- 可以按索引位置,可以按照列名
例子
step 1 chu创建mysql数据库(在navicat中)
连接名为test 密码为12345
注意如果设置的密码与之前mysql密码不同会出现报错
在DB连接下新建数据库test(注意后面连接要的是数据库名)
在数据库test中创建表t_book (int varchar double)
注意:使用java.sql包
查询数据库
package DataBase;
//注意都是java.sql.*下的
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class SelectTest {
public static void main(String[] args) {
// 构建数据库与Java之间的桥梁
try {
// 完成类的加载 com.mysql.jdbc.Driver类来源于(pom文件)的jar包
Class.forName("com.mysql.jdbc.Driver");
System.out.println("注册驱动成功");
} catch (ClassNotFoundException e) {
System.out.println("注册驱动失败");
e.printStackTrace();
return ;
}
// 定义连接字符串 test是数据库名 不是连接名也不是表名
String url = "jdbc:mysql://localhost:3306/test";
Connection conn = null;
try {
// 构建桥梁 url 用户名密码(mysql数据库的) 这里好像永远都是root
conn = DriverManager.getConnection(url, "root", "12345");
// 创建数据库执行者
Statement stmt = conn.createStatement();
System.out.println("创建Statement成功!");
// 执行SQL语句并返回结果到ResultSet
ResultSet rs = stmt.executeQuery("select tookid, bookname, price from t_book order by tookid");
// 开始遍历ResultSet数据
while(rs.next()) {
System.out.println(rs.getInt(1)+","+rs.getString(2)+","+rs.getDouble("price"));
}
rs.close();
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(null != conn) {
try {
conn.close();
System.out.println("成功关闭连接");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
增删改数据库
import java.sql.*;
public class UpdateTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
executeUpdate("update t_book set price = 300.0 where tookid = 101");
executeUpdate("insert into t_book(tookid, bookname, price) values(104, '编译原理', 90)");
executeUpdate("delete from t_book where tookid = 102");
}
public static void executeUpdate(String sql) {
// 构建数据库与Java之间的桥梁
try {
// 完成类的加载 com.mysql.jdbc.Driver类来源于(pom文件)的jar包
Class.forName("com.mysql.jdbc.Driver");
System.out.println("注册驱动成功");
} catch (ClassNotFoundException e) {
System.out.println("注册驱动失败");
e.printStackTrace();
return ;
}
String url = "jdbc:mysql://localhost:3306/test";
Connection conn = null;
try {
// 构建桥梁 url 用户名密码(mysql数据库的)
conn = DriverManager.getConnection(url, "root", "12345");
// 创建数据库执行者
Statement stmt = conn.createStatement();
System.out.println("创建Statement成功!");
// 执行SQL语句
int result = stmt.executeUpdate(sql);
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(null != conn) {
try {
conn.close();
System.out.println("成功关闭连接");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
JDBC高级操作
---------------------------------JDBC事务 -----------------------------------
事务:
数据库事务DataBase Transaction
作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行
事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性
JDBC事务
- 关闭自动提交(jdbc默认自动提交),实现多语句同一事务
- connection.setAutoCommit(false);
- connection.commit(); 提交事务
- connection.rollback(); 回滚事务 (上述语句全部废弃不执行)
- 保存点机制
···connection.setSavepoint()
···connection.rollback(Savepoint)
例子
清空DataBase后运行
public class TransactionTest {
// JDBC事务处理
public static void main(String[] args) {
try {
// 完成类的加载 com.mysql.jdbc.Driver类来源于(pom文件)的jar包
Class.forName("com.mysql.jdbc.Driver");
System.out.println("注册驱动成功");
} catch (ClassNotFoundException e) {
System.out.println("注册驱动失败");
e.printStackTrace();
}
// 定义连接字符串 test是数据库名 不是连接名也不是表名
String url = "jdbc:mysql://localhost:3306/test";
Connection conn = null;
try {
// 构建桥梁 url 用户名密码(mysql数据库的)
conn = DriverManager.getConnection(url, "root", "12345");
conn.setAutoCommit(false); // 关闭自动提交
insertBook(conn, "insert into t_book values(105, 'qqqq', 10)");
insertBook(conn, "insert into t_book values(106, 'wwww', 10)");
insertBook(conn, "insert into t_book values(107, 'eeee', 10)");
Savepoint phase1 = conn.setSavepoint(); // 设置一个保存点
insertBook(conn, "insert into t_book values(108, 'wwww', 10)");
insertBook(conn, "insert into t_book values(109, 'eeee', 10)");
conn.rollback(phase1); // 回滚到phase1保存点,即上面两行无效
conn.commit(); // 注意一条语句如果被回滚就无法再tcommit故上述两行无效
System.out.println("操作成功");
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(null != conn) {
try {
conn.close();
System.out.println("成功关闭连接");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void insertBook(Connection conn, String sql) {
try {
// 构建数据库执行者
Statement stmt = conn.createStatement();
System.out.println("创建Statement成功!");
// 执行SQL语句
int result = stmt.executeUpdate(sql);
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果
------------------------PreparedStatement--------------------------
拼接字符串有风险
如恶意SQL语句(注入攻击)
PreparedStatement
- Java提供PreparedStatement,更为安全执行SQL
- 和Statement区别是使用“?”代替字符串拼接
- 使用setXXX(int,Object)的函数来实现对于?的替换
—注:不需要考虑字符串的两侧单引号
—参数赋值,清晰明了,拒绝拼接错误 - 提供addBatch批量更新功能
- Select语句一样用ResultSet接受结果
如果有大量的sql语句,它们结构相同,仅仅差别在具体数值上,可以通过addBatch方法进行批量操作。这样会提高性能,减少数据库负担。
如下述例子中的patchInsertBook()函数,
这样操作的效率要高于采用executeUpdate(要执行十次)。
public class InsertTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
InsertBook();
patchInsertBook();
}
public static void InsertBook() {
try {
// 完成类的加载 com.mysql.jdbc.Driver类来源于(pom文件)的jar包
Class.forName("com.mysql.jdbc.Driver");
System.out.println("注册驱动成功");
} catch (ClassNotFoundException e) {
System.out.println("注册驱动失败");
e.printStackTrace();
}
String url = "jdbc:mysql://localhost:3306/test";
Connection conn = null;
try {
// 构建桥梁 url 用户名密码(mysql数据库的)
conn = DriverManager.getConnection(url, "root", "12345");
String sql = "insert into t_book(tookid, bookname, price) values(?,?,?)";
// 创建数据库执行者
PreparedStatement pstmt = conn.prepareStatement(sql);
int tookid = 0507;
String bookName = "初九";
double price = 1000000;
pstmt.setInt(1, tookid);
pstmt.setString(2, bookName);
pstmt.setDouble(3, price);
int result = pstmt.executeUpdate();
System.out.println("操作成功");
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(null != conn) {
try {
conn.close();
System.out.println("成功关闭连接");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void patchInsertBook() {
// 批量插入
try {
// 完成类的加载 com.mysql.jdbc.Driver类来源于(pom文件)的jar包
Class.forName("com.mysql.jdbc.Driver");
System.out.println("注册驱动成功");
} catch (ClassNotFoundException e) {
System.out.println("注册驱动失败");
e.printStackTrace();
}
String url = "jdbc:mysql://localhost:3306/test";
Connection conn = null;
try {
// 构建桥梁 url 用户名密码(mysql数据库的)
conn = DriverManager.getConnection(url, "root", "12345");
String sql = "insert into t_book(tookid, bookname, price) values(?,?,?)";
// 创建数据库执行者
PreparedStatement pstmt = conn.prepareStatement(sql);
String bookName = "十九";
double price = 57;
for(int i = 200; i < 210; i++) {
pstmt.setInt(1, i);
pstmt.setString(2, bookName);
pstmt.setDouble(3, price);
pstmt.addBatch(); // 批量保存
}
pstmt.executeBatch();
System.out.println("操作成功");
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(null != conn) {
try {
conn.close();
System.out.println("成功关闭连接");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
------------------------ResultSetMetaData--------------------------
-
ResultSet可以用来承载所有的select语句返回的结果集
-
ResultSetMetaData来获取ResultSet返回的属性(eg:每行名字类型等等)
(1) getColumnCount() 返回结果的列数
(2) getColumnClassName(i) 返回第i列的数据的java类名 )getColumnName,getClassName
(3) getColumnTypeName(i) 返回第i列的数据库类型名称getColumnName,getTypeName
(4) getColumnType(i) 返回第i列SQL类型
getColumnName,getColumnType
(5)getColumnName(i) 返回第i列的列名 (bookid)
注意JDBC中很多操作从1开始而非0
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
public class ResultSetMetaDataTest {
public static void main(String[] args) {
try {
// 完成类的加载 com.mysql.jdbc.Driver类来源于(pom文件)的jar包
Class.forName("com.mysql.jdbc.Driver");
System.out.println("注册驱动成功");
} catch (ClassNotFoundException e) {
System.out.println("注册驱动失败");
e.printStackTrace();
}
String url = "jdbc:mysql://localhost:3306/test";
Connection conn = null;
try {
// 构建桥梁 url 用户名密码(mysql数据库的)
conn = DriverManager.getConnection(url, "root", "12345");
// 创建数据库执行者
Statement stmt = conn.createStatement();
System.out.println("创建Statement成功!");
// 执行SQL语句并返回结果到ResultSet
ResultSet rs = stmt.executeQuery("select tookid, bookname, price from t_book order by tookid");
// 获取结果集的元数据
ResultSetMetaData meta = rs.getMetaData();
int cols = meta.getColumnCount(); // 有多少列
for(int i = 1; i <= cols; i++) {
System.out.println(meta.getColumnName(i) + " , " + meta.getColumnTypeName(i));
}
rs.close();
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(null != conn) {
try {
conn.close();
System.out.println("成功关闭连接");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
数据库连接池
运用共享技术来实现数据库连接池(享元模式)
(桥梁构建不容易,单次使用成本高)
- 降低系统中数据库连接Connection对象的数量
- 降低数据库服务器的连接响应消耗
- 提高Connction获取的响应速度
享元模式:一个系统中存在大量的相同的对象,由于这类对象的大量使用,会造成系统内存的耗费,可以使用享元模式减少系统中对象的数量
C3P0数据库连接池
Druid数据库连接池