第七章. Java 数据库编程
Java 数据库编程
1. 数据库概论
见数据库专栏
2. JDBC基本操作
2.1 JDBC
Java和数据库是平行的两套系统
Java和数据库的连接方法
- JDBC (主流): Java Database Connectivity
– JDBC 1, JDK 1.1
– JDBC 2 JDK 1.3~JDK1.4
– JDBC 3 JDK 5
– JDBC 4 JDK 6, (JDK 7, JDBC4.1; JDK8, JDBC4.2)
2.2 JDBC 操作
java.sql., javax.sql.: 这2个包只是接口类
– 根据数据库版本和JDBC版本合理选择
– 一般数据库发行包都会提供jar包,同时也要注意区分32位
和64位(数据库分32/64位, JDK也分32/64位)。
连接字符1串(数据库连接)
– jdbc:oracle:thin:@127.0.0.1:1521:dbname
– jdbc:mysql://localhost:3306/mydb
– jdbc:sqlserver://localhost:1433; DatabaseName=dbname
Java连接数据库操作步骤:
- 构建连接(搭桥)
– 注册驱动,寻找材质, class.forName(“…”); //Oracle,Mysql ,SQL server…
– 确定对岸目标 , 建桥 Connection - 执行操作(派个人过桥, 提着篮子,去拿数据)
– Statement (执行者)
– ResultSet(结果集) - 释放连接(拆桥)
– connection.close();
Statement :执行操作
- Statement 执行者类
– 使用executeQuery()执行select语句,返回结果放在ResultSet
– 使用executeUpdate()执行insert/update/delete,返回修改的行数
– 一个Statement对象一次只能执行一个命令 - ResultSet 结果对象
– next() 判断是否还有下一条记录
– getInt/getString/getDouble/……
• 可以按索引位置,可以按照列名
注意事项:
- ResultSet不能多个做笛卡尔积连接
- ResultSet最好不要超过百条,否则极其影响性能
- ResultSet也不是一口气加载所有的select结果数据
- Connection 很昂贵,需要及时close
- Connection所用的jar包和数据库要匹配
JDBC的查找,更新:
import java.sql.*;
public class SelectTest {
public static void main(String[] args){
//构建Java和数据库之间的桥梁介质
try{
Class.forName("com.mysql.jdbc.Driver");
System.out.println("注册驱动成功!");
}catch(ClassNotFoundException e1){
System.out.println("注册驱动失败!");
e1.printStackTrace();
return;
}
String url="jdbc:mysql://localhost:3306/test"; //localhost为IP地址
Connection conn = null;
try {
//构建Java和数据库之间的桥梁:URL,用户名,密码
conn = DriverManager.getConnection(url, "root", "123456");
//构建数据库执行者
Statement stmt = conn.createStatement();
System.out.println("创建Statement成功!");
//执行SQL语句并返回结果到ResultSet
ResultSet rs = stmt.executeQuery("select bookid, bookname, price from book order by bookid");
//开始遍历ResultSet数据
while(rs.next())
{
System.out.println(rs.getInt(1) + "," + rs.getString(2) + "," + rs.getInt("price"));
}
rs.close();
stmt.close();
} catch (SQLException e){
e.printStackTrace();
}
finally
{
conn.close();
}
}
3. JDBC高级操作
3.1 事务
数据库事务(Database Transaction):
- 数据库运行中的逻辑工作单位,执行的一系列操作,要么完全地执行,要么完全地不执行。
- 必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性
JDBC事务:
- 关闭自动提交,实现多语句同一事务:
– connection.setAutoCommit(false); - connection.commit(); 提交事务
- connection.rollback(); 回滚事务
- 保存点机制
– connection.setSavepoint()
– connection.rollback(Savepoint)
执行部分:
public static void insertBook(Connection conn, String sql) {
try {
// 构建数据库执行者
Statement stmt = conn.createStatement();
// 执行SQL语句
int result = stmt.executeUpdate(sql);
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
try {
// 构建Java和数据库之间的桥梁:URL,用户名,密码
conn = DriverManager.getConnection(url, "root", "123456");
conn.setAutoCommit(false);//取消自动提交
insertBook(conn, "insert into t_book values(101, 'aaaa', 10)");
insertBook(conn, "insert into t_book values(102, 'bbbb', 10)");
insertBook(conn, "insert into t_book values(103, 'cccc', 10)");
Savepoint phase1 = conn.setSavepoint(); //设置一个保存点
insertBook(conn, "insert into t_book values(104, 'cccc', 10)");
insertBook(conn, "insert into t_book values(105, 'cccc', 10)");
conn.rollback(phase1); //回滚到phase1保存点,即上面2行无效
conn.commit();
System.out.println("操作成功");
}
运行结果:
3.2 PreparedStatement
Java提供PreparedStatement :进行SQL语句的拼接,传参
- 使用“?” 代替字符串拼接
- 使用setXXX(int,Object)的函数来实现对于“?”的替换
- 提供addBatch批量更新功能
–只提交,不执行
–当有大量sql语句结构相同,仅有值不同时,可以通过addBatch操作提高性能
try {
//构建Java和数据库之间的桥梁:URL,用户名,密码
conn = DriverManager.getConnection(url, "root", "123456");
//三个问号分别对应括号的bookid,bookname,price(形参)
String sql = "insert into t_book(bookid,bookname,price) values(?,?,?)";
//构建数据库执行者
PreparedStatement pstmt = conn.prepareStatement(sql);
//执行SQL语句
int bookid = 10;
String bookName = "Effective Java',50);delete from t_book;insert into t_book values(101, 'faked book";
int price = 50;
//values(1, 'Effective Java', 50)
String bookName = "aaaaaaaaaaaaaaaa";
int price = 50;
//values(1, 'Effective Java', 50)
for(int i=200;i<210;i++)
{
pstmt.setInt(1, i);
pstmt.setString(2, bookName);
pstmt.setInt(3, price);
pstmt.addBatch();//只提交,不执行
}
pstmt.executeBatch();
int result = pstmt.executeUpdate(); //执行
pstmt.close();
System.out.println("操作成功");
}
3.3 ResultSetMetaData
- ResultSet可以用来承载所有的select语句返回的结果集
- ResultSetMetaData来获取ResultSet返回的属性(如,每一行
的名字类型等)
– getColumnCount(),返回结果的列数
– getColumnName(), 返回第i列列名
– getColumnClassName(i),返回第i列的数据的Java类名
– getColumnTypeName(i),返回第i列的数据库类型名称
– getColumnType(i),返回第i列的SQL类型
– …
ResultSet rs = stmt.executeQuery("select bookid, bookname, price from t_book order by bookid");
//获取结果集的元数据
ResultSetMetaData meta = rs.getMetaData();
int cols = meta.getColumnCount();
for(int i=1;i<=cols;i++)
{
System.out.println(meta.getColumnName(i) + "," + meta.getColumnTypeName(i));
}
4. 数据库连接池
:构建连接与断开连接的成本很高
- 运用共享技术来实现数据库连接池(享元模式)
– 降低系统中数据库连接Connection对象的数量
– 降低数据库服务器的连接响应消耗
– 提高Connection获取的响应速度
数据库连接池
- 理解池Pool的概念
– 初始数、最大数、增量、超时时间等参数。 - 常用的数据库连接池
– DBCP (Apache, http://commons.apache.org/,性能较差)
– C3P0 (https://www.mchange.com/projects/c3p0/)
– Druid (Alibaba, https://github.com/alibaba/druid) //性能最好
–
• driverClass: 驱动class,这里为mysql的驱动
• jdbcUrl: jdbc链接
• user password:数据库用户名,密码
• initialPoolSize 初始数量:一开始创建多少条链接
• maxPoolSize 最大数:最多有多少条链接
• acquireIncrement 增量:用完每次增加多少个
• maxIdleTime最大空闲时间:超出的链接会被抛弃 - 数据库连接池是对connection进行管理,而不是产生,产生要通过Mysql.Driver
Druid连接的获取:
conn = DruidFactory1.getConnection(); //工厂模式1
conn = DruidFactory2.getConnection(); //工厂模式2
Druid工厂模式1:在Java文件中配置初始化信息
public class DruidFactory1 {
private static DruidDataSource dataSource = null;
public static void init() throws Exception {
dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test");
dataSource.setInitialSize(5);
dataSource.setMinIdle(1);
dataSource.setMaxActive(10);
// 启用监控统计功能 dataSource.setFilters("stat");//
}
public static Connection getConnection() throws Exception {
if(null == dataSource)
{
init();
}
return dataSource.getConnection();
}
}
Druid工厂模式2:通过Properties文件进行配置:
public class DruidFactory2 {
private static DruidDataSource dataSource = null;
public static void init() throws Exception {
Properties properties = new Properties();
InputStream in = DruidFactory2.class.getClassLoader().getResourceAsStream("druid.properties");
properties.load(in);
dataSource = (DruidDataSource)DruidDataSourceFactory.createDataSource(properties);
in.close();
}
public static Connection getConnection() throws Exception {
if(null == dataSource)
{
init();
}
return dataSource.getConnection();
}
}
Properties文件: