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','xiaoming@qq.com'),
(2, 'xiaozhang', '123456', 'xiaozhang@qq.com');
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','Tom@qq.com'),('Anny', '123456', 'Anny@qq.com')";
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='Tomm@163.com' 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','Tom@qq.com'),('Anny', '123456', 'Anny@qq.com')";
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;
}
}
这里使用了静态代码块