JDBC编程
目前市面上数据库管理系统已经非常多,mysql,Oracle,SQLserver等等
在ODBC出现之前,对这些数据库的访问是一件非常麻烦的事情,因为这些数据库虽然都支持sql,但他们针对自己数据库的访问方法,所以当用户访问不同个的数据库时,就必须使用不同API来编写相应的数据库访问程序
- ODBC(Open Database Connectivity)
通过ODBC访问不同的数据库,无需对数据库访问程序进行修改,这样ODBC的应用越来越广泛 - JDBC(java data base connectivity)
按照ODBC的模式来制定的,它是一个通用的底层的支持sql功能的Java API
JDBC的组成
JDBC提供了两种接口
- JDBC API
- 面向开发人员的API
- JDBC Driver API
- 面向底层驱动程序开发商的API
JDBC API
JDBC API 是一系列的应用编程接口,可以用来进行数据库连接,访问数据等
JDBC API的主要编程接口:
- DriverManager
- 驱动程序管理类
- 用来装载驱动程序,并为创建数据库链接提供支持
- Connection
- 是一个接口
- 用来连接某一个指定的数据库
- Statement
- 是一个接口
- 提供了执行SQL语句,获取查询结果的方法
- PreparedStatement
- 用于执行预编译的SQL语句
- ResultSet
- 提供了对接口集进行处理的方法
JDBC Driver API
主要有四种类型
- JDBC-ODBC bridge
- 通过将JDBC的调用全部委托给其他编程接口来实现
- 部分java技术的本地API驱动程序
- 驱动程序部分实现通过JAVA语言
- 其他的部分则委托给本地的数据库的客户段代码来实现
- 全部基于java技术的本地API程序
- 这种驱动程序的实现全部通过Java语言
- 通常由某个中间件服务器提供
- 客户端程序可以使用数据库无关的协议和中间件服务器进行通信
- 中间件服务器再讲客户端的调用转发给服务器进行处理
- 全部基于java技术的本地协议驱动程序
- 全部基于java语言
- 包含了特定数据库的访问协议,使得客户端可以直接同服务器进行通信
使用JDBC进行增删改
首先建表
-
修改Mysql-WorkBench快捷键
- 自动补全 ctrl+space
- 但是和Ubuntu的输入法切换冲突
- 修改/usr/share/mysql-workbench/data/main_menu.xml文件
- modifier + Space 就是 Ctrl+space的意思,修改为不冲突的快捷键例如F2
- 或者 直接 菜单栏 edit->auto complete 启动自动补全
- 自动补全 ctrl+space
-
创建用户表
use jsp_db; create table tbl_user( id int(11) unsigned not null auto_increment, name varchar(50) not null default '', password varchar(50) not null default '', email varchar(50) default '', primary key(id) )engine=InnoDB default charset=utf8; -
创建地址表
CREATE TABLE tbl_address ( id INT(11) UNSIGNED not null auto_increment, city varchar(20) default null, country varchar(20) default null, user_id int(11) unsigned not null, primary key(id) ) engine=InnoDB default charset = utf8; -
表中插入记录
insert into tbl_user(id,name,password,email) values (1, 'xiaoming', '123456','[email protected]'), (2, 'xiaozhang', '123456', '[email protected]'); insert into tbl_address(city, country, user_id) values ('beijing', 'china', 1), ('tianjin', 'china', 2);
查询初体验
- JDBC执行流程
-
Eclise中
- Alt+ ‘/’ 可以进行生成函数
-
数据库查询
package com.JDBC.Test; import java.sql.DriverManager; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import com.mysql.jdbc.Driver; public class JDBCTest { public static void main(String[] args) { String sql = "select * from tbl_user"; Connection connection = null; Statement statement = null; ResultSet resultSet = null; try { // 用于注册Mysql JDBC的驱动程序 // forName 方法用于初始化参数指定的类,并创建一个对应的实例对象 Class.forName("com.mysql.jdbc.Driver"); // 这里url 指定了 数据库的 地址 端口 以及具体访问的库名 String url = "jdbc:mysql://localhost:3306/jsp_db"; String user = "root"; String password = "root"; // 获取Mysql 数据库的连接 这里使用的是 DriverManage 的 getConnection 方法 connection = DriverManager.getConnection(url, user, password); // 创建一个Statement对象 statement = connection.createStatement(); // 使用Statement对象的executeQuery方法来发送Sql语句 // executeQuery 方法返回一个 ResultSet对象 resultSet = statement.executeQuery(sql); // 遍历ResultSet对象 while (resultSet.next()) { System.out.println(resultSet.getInt("id")); System.out.println(resultSet.getString("name")); System.out.println(resultSet.getString("password")); System.out.println(resultSet.getString("email")); } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } finally { try { // 关闭ResultSet对象的结果集 resultSet.close(); } catch (Exception e2) { } try { // 关闭Statement对象 statement.close(); } catch (Exception e2) { } try { // 关闭数据库连接 connection.close(); } catch (Exception e2) { } } } }
增删改查
-
首先写一个Connection的函数,这样执行就不用每次都写 那几条固定语句
public static Connection getConnection() { Connection conn = null; try { Class.forName("com.mysql.jdbc.Driver"); // 这里url 制定了 访问数据库的 地址 端口 以及 具体 库的名字 String url = "jdbc:mysql://localhost:3306/jsp_db"; String user = "root"; String password = "root"; // 获取Mysql 数据库的连接 这里使用的是 DriverManage 的 getConnection 方法 conn = DriverManager.getConnection(url, user, password); } catch (Exception e) { // TODO: handle exception } return conn; } -
然后就是增删改的函数了 -
增
public static void insert() { Connection conn = getConnection(); try { // 存储sql语句,用来向用户表中插入记录 String sql = "insert into tbl_user(name,password,email)" + "values" +"('Tom', '123456','[email protected]'),('Anny', '123456', '[email protected]')"; Statement st = conn.createStatement(); // Statement中的executeUpdate方法,可以执行DML语句,包括insert update 以及 delete // 也可以执行没有返回结果的语句 例如:DDL语句 // 参数是一个字符串形式的sql语句,如果执行的是DML语句,那么返回影响的记录条数,如果是DDL语句则返回0 // 会抛出sqlExcuption 以及 sqlTimeOut 的异常 int count = st.executeUpdate(sql); System.out.println("向表中插入了" + count + "条语句"); conn.close(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } -
改
public static void update() { Connection conn = getConnection(); try { // 存储sql语句,用来向用户表中插入记录 String sql = "update tbl_user set email='[email protected]' where name = 'Tom'"; Statement st = conn.createStatement(); // Statement中的executeUpdate方法,可以执行DML语句,包括insert update 以及 delete // 也可以执行没有返回结果的语句 例如:DDL语句 // 参数是一个字符串形式的sql语句,如果执行的是DML语句,那么返回影响的记录条数,如果是DDL语句则返回0 // 会抛出sqlExcuption 以及 sqlTimeOut 的异常 int count = st.executeUpdate(sql); System.out.println("向表中更新了" + count + "条语句"); conn.close(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } -
删
public static void delete_db() { Connection conn = getConnection(); try { // 存储sql语句,用来向用户表中插入记录 String sql = "delete from tbl_user where name='Tom'"; Statement st = conn.createStatement(); // Statement中的executeUpdate方法,可以执行DML语句,包括insert update 以及 delete // 也可以执行没有返回结果的语句 例如:DDL语句 // 参数是一个字符串形式的sql语句,如果执行的是DML语句,那么返回影响的记录条数,如果是DDL语句则返回0 // 会抛出sqlExcuption 以及 sqlTimeOut 的异常 int count = st.executeUpdate(sql); System.out.println("向表中删除了" + count + "条语句"); conn.close(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } -
之后只需要在 main 函数中 调用inset 等 方法就 能插入 修改 删除了
-
需要注意的是,不要把sql语句写错
-
执行之后,控制台会输出修改的语句的个数
-
main方法
public static void main(String[] args) { //insert(); //update(); delete_db(); }
JDBC事务处理
数据库是一个多用户使用的共享资源
当多个用户使用数据库存取资源的时候,就会产生不同用户存取同一数据的情况
因此需要控制并发
-
原子性
- 事务中包含的操作都被看做是一个逻辑单元
- 这个逻辑单元的操作 要么全部成功 要么全部失败
- 事务中所有元素作为一个整体,提交或回滚
- 事务的所有元素是不可分割的,是一个完整的操作
-
一致性
- 事务开始之前和事务结束以后,数据库都处于一致性状态
- 数据库的完整性约束,没有被破坏
-
隔离性
- 对数据库进行修改的多个事务,是彼此隔离的
- 事务必须是独立的,不应该以任何形式影响其他事务
-
持久性
- 事务完成之后,对于系统的影响是永久的
- 该修改真实的修改了数据库,即使系统出现故障也会一直保留
事务语句
- 开始事务
- Begin transaction
- 提交事务
- Commit transaction
- 回滚事务
- Rollback transaction
举个例子:
我们有 user 和 address 表
在 address 表中 插入 Tom 的地址信息
在 user 表中 插入 id 为 1 的 Tom的个人信息
- 不难发现,由于user表中原来就有id 为 1 的信息,所以user表插入失败
- 但是,address 表却可以插入,因为没有主键冲突
- 这就是
完整性 缺失
代码实例
```
public class TransAction {
public static Connection getConnection() {
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/jsp_db";
String user = "root";
String password = "root";
conn = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
}
return conn;
}
// 使用 throws SQLException 来捕获异常,因为如果数据插入失败的时候会抛出异常
// 通过 这种方法 将异常传递给 上层函数 通过上层函数捕捉异常 进行回滚
public static void insertUser(Connection conn) throws SQLException {
String sql = "insert into tbl_user(name,password,email)"
+ "values"
+"('Tom', '123456','[email protected]'),('Anny', '123456', '[email protected]')";
Statement st = conn.createStatement();
int count = st.executeUpdate(sql);
System.out.println("向表中插入了" + count + "条语句");
}
public static void insertAddress(Connection conn) throws SQLException {
String sql = "insert into tbl_address(id,city,country)"
+ "values"
+"(1, 'beijing','china'),(2, 'tianjing', 'china')";
Statement st = conn.createStatement();
int count = st.executeUpdate(sql);
System.out.println("向表中插入了" + count + "条语句");
}
public static void main(String[] args) {
Connection conn = null;
try {
conn = getConnection();
//关闭自动提交
conn.setAutoCommit(false);
insertAddress(conn);
insertUser(conn);
conn.commit();
} catch (SQLException e) {
System.out.println("=====偶哟,捕获到SQL异常了呢====");
e.printStackTrace();
try {
// 如果捕获到异常 那么说明数据插入失败,则要回滚到插入之前的状态
// 避免出现,部分插入,部分没插入 所导致的 完整性缺失问题
System.out.println("====现在开始数据回滚呢,请等会哦====");
conn.rollback();
System.out.println("====回滚成功呢,请再看看是哪里的代码有问题呢====");
} catch (Exception e2) {
e2.printStackTrace();
}
} finally {
try {
// 如果 conn 连接 不为空的时候,最后要关闭连接
if (conn != null) {
conn.close();
}
} catch (Exception e3) {
e3.printStackTrace();
}
}
}
}
```
JDBC 优化
前面的写法都是直接将 数据库链接,用户名,密码等直接内嵌到代码中
但是这样的写法其实重用性特别差,一旦修改了密码那么所有的文件都需要修改
这个时候最好的方法就是写一个 配置文件,然后所有的数据链接都用这个配置文件
这里 默认 配置文件的后缀是 .properties
```
driver=com.mysql.jdbc.Driver
dburl=jdbc\:mysql\://localhost\:3306/jsp_db
user=root
password=root
```
直接写上面的内容就行了 不需要上面花里胡哨的
```
package com.JDBC.Test;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
import com.mysql.jdbc.Driver;
public class DataConnectFactory {
private static String driver;
private static String dburl;
private static String user;
private static String password;
private static final DataConnectFactory factory = new DataConnectFactory();
private Connection connection;
static {
Properties prop = new Properties();
try {
InputStream in = DataConnectFactory.class.getResourceAsStream("dbconfig.properties");
prop.load(in);
} catch (Exception e) {
System.out.println("==配置问价出错了呢==");
}
driver = prop.getProperty(driver);
dburl = prop.getProperty(dburl);
password = prop.getProperty(password);
user = prop.getProperty(user);
}
//定义默认构造函数
private DataConnectFactory(){
}
// 单例模式
public static DataConnectFactory getInstance() {
return factory;
}
public Connection makeConnection() {
try {
Class.forName(driver);
connection = DriverManager.getConnection(dburl, user, password);
} catch (Exception e) {
e.printStackTrace();
}
return connection;
}
}
```
这里使用了静态代码块