文章目录
一、JDBC
1.1 JDBC介绍
JDBC(Java Data Base Connectivity )Java连接数据库。
JDBC 是 Java 访问数据库的标准规范,真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。所以我们只需要会调用 JDBC 接口中的方法即可,数据库驱动由数据库厂商提供。
- 使用 JDBC 的好处:
(1)如果要开发访问数据库的程序,只需要调用 JDBC 接口中的方法即可,不用关注类是如何实现的。
(2)同一套 Java 代码,进行少量的修改就可以访问其他 JDBC 支持的数据库
1.2 JDBC的入门使用
1.2.1 JDBC开发使用的包
包名 | 作用 |
---|---|
java.sql | 所有与 JDBC 访问数据库相关的接口和类 |
javax.sql | 数据库扩展包,提供数据库额外的功能。 |
数据库驱动 | 对JDBC接口的实现类 |
1.2.2 JDBC的核心API
接口 | 作用 |
---|---|
DriverManager 类 | 管理注册数据库驱动,得到Connection连接对象 |
Connection 接口 | 一个连接对象,可用于创建 Statement 和 PreparedStatement 操作对象 |
Statement 接口 | SQL语句对象,可以发送SQL语句 |
PreparedStatemen 接口 | SQL语句对象,预编译操作 |
ResultSet 接口 | 封装数据库查询的结果集,返回给客户端 Java 程序 |
1.2.3 JDBC的入门
使用步骤:
1. 导入数据库的驱动jar包,并且依赖jar包。
2. 注册驱动
3. 获取连接对象
4. 获取操作对象
5.开始操作
6.释放资源
注册驱动:Class.forName( 数据库驱动实现类)。
比如:MySQL数据库的实现类是“com.mysql.jdbc.Driver”,这里我们就可以使用Class.forName(“com.mysql.jdbc.Driver”);
DriverManager 类:管理和注册驱动,并且创建数据库的连接。
我们调用其中的getConnection(String url, String user, String password)方法,通过连接字符串,用户名,密码来得到数据库的连接对象
参数说明:
参数 | 说明 |
---|---|
连接字符串 URL | 不同的数据库 URL 是不同的 |
user | 数据库登录的用户名 |
password | 数据库登录的密码 |
URL的地址格式:协议名: 子协议:// 服务器名或 IP 地址: 端口号/ 数据库名? 参数=参数值。通过URL地址获取连接数据库的名称
MySQL的写法:jdbc:mysql://localhost:3306/数据库[?参数名=参数值]
简写形式:jdbc:mysql:/// 。前提是必须为本地的服务器
Connection 接口:代表一个连接对象,具体的实现类由数据库的厂商实现。
Statement接口:代表一条语句对象,用于发送 SQL 语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象。
Statement 中的方法(开始操作):
int executeUpdate(String sql) 用于发送 DML 语句,增删改的操作,insert、update、delete。
参数:SQL 语句。
返回值:返回对数据库影响的行数。
ResultSet executeQuery(String sql) 用于发送 DQL 语句,执行查询的操作。select。
参数:SQL 语句。
返回值:查询的结果集
释放资源:
1.需要释放的对象:ResultSet 结果集,Statement 语句,Connection 连接
2.释放的顺序:先开的后关,后开的先关。ResultSet Statement Connection
3.释放资源的代码可以放到finally代码块中。
1.2.4 执行增删改数据库的代码
public class JDBCtest1 {
public static void main(String[] args)throws Exception {
// 1.依赖jar包
//2.注册驱动 使用反射 com.mysql.jdbc.Driver
Class.forName("com.mysql.jdbc.Driver");
//3.和数据库建立连接
// DriverManager的getConnection方法建立连接
//url:jdbc:mysql://localhost.3306/mydemo
//连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydemo", "root", "123456");
//4.操作对象
Statement statement = conn.createStatement();
//sql语句
String sql="insert into user values(100,'田七')";
//5.执行语句 返回值是影响的行数
int i = statement.executeUpdate(sql);
if (i>0){
System.out.println("连接成功");
}else{
System.out.println("连接失败");
}
//释放资源
conn.close();
statement.close();
}
}
1.2.5 执行查询数据库的代码
创建一个User类,用来封装数据库中的数据为对象
public class User {
//成员变量要和数据库中的字段名对应
private Integer id;
private String username;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
'}';
}
}
查询数据库中的数据
public class JDBCtest2 {
public static void main(String[] args)throws Exception {
//创建一个arraylist集合
ArrayList<User> list = new ArrayList<>();
Class.forName("com.mysql.jdbc.Driver");
//连接
Connection coon = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydemo", "root", "123456");
//操作对象
Statement statement = coon.createStatement();
String sql="select * from user";
//查询
ResultSet resultSet = statement.executeQuery(sql);
//resultSet 集合 next方法 判断是否存在下一行
while (resultSet.next()){
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
//封装到User对象中
User user = new User();
user.setId(id);
user.setUsername(username);
list.add(user);
}
System.out.println(list);
//释放资源
coon.close();
statement.close();
resultSet.close();
}
}
ResultSet接口 :用来封装数据库查询的结果集,我们可以对结果集进行遍历,取出每一条记录。
方法:
boolean next() 返回值为Boolean类型,如果还有下一条数据,返回true,否则返回false
数据类型 getXxx() : 参数为String类型,对应数据库中的字段名
使用完毕以后要关闭结果集 ResultSet,再关闭 Statement,再关闭 Connection
1.2.6 预编译操做对象 PreparedStatement
出现的前提:之前的实验SQL语句是进行字符串的拼接,用户输入的内容作为了 SQL 语句语法的一部分,改变了原有 SQL 真正的意义,以上问题称为 SQL 注入。要解决 SQL 注入就不能让用户输入的密码和我们的 SQL 语句进行简单的字符串拼接。
获取PreparedStatement对象:通过Connection对象调用prepareStatement()方法,参数传递SQL语句。
特点:
- SQL语句中的字段对应的值可以使用?进行占位
- 使用PreparedStatement中的setxxx方法可以给字段进行赋值
//字段对应的值可以使用?进行占位
String sql = "select * from users where username=? and password=?";
//Connection对象调用prepareStatement()方法
PreparedStatement preparedStatement = conn.prepareStatement(sql);
//设置值 第一个参数为赋值的占位符,第二个参数为值
preparedStatement.setString(1,username);
preparedStatement.setString(2,password);
//查询
ResultSet resultSet = preparedStatement.executeQuery();
好处:
- prepareStatement()会先将 SQL 语句发送给数据库预编译。PreparedStatement 会引用着预编译后的结果。可以多次传入不同的参数给 PreparedStatement 对象并执行。减少 SQL 编译次数,提高效率。
- 安全性更高,没有 SQL 注入的隐患。
- 提高了程序的可读性
二、工具类
上面写的代码出现了很多的重复代码,我们可以这些公共代码抽取出来,作为一个工具类。可以在不同的地方复用。
创建工具类Utils 其中包含注册驱动,获取连接,释放资源,可以抽取为工具类。
1.静态代码块
public class Utils1 {
private static String url;
private static String user;
private static String password;
//私有化构造方法
private Utils1(){}
//读取配置文件中数据库的配置
static {
try {
Class.forName("com.mysql.jdbc.Driver");
url = "jdbc:mysql://localhost:3306/mydemo";
user = "root";
password = "123456";
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接对象的方法
public static Connection getConnection() throws SQLException {
Connection connection = DriverManager.getConnection(url, user, password);
return connection;
}
//释放资源的方法
public static void close(Connection conn, Statement statement, ResultSet resultSet) throws SQLException {
conn.close();
statement.close();
resultSet.close();
}
public static void close(Connection conn,Statement statement) throws SQLException {
conn.close();
statement.close();
}
}
另一种我们可以通过将数据加载到配置文件里面,改变数据库的时候只需要修改配置文件中的即可。
2.配置文件:JDBC.properties
driverName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydemo
user=root
password=123456
public class Utils2 {
//定义成员变量
private static String url;
private static String user;
private static String password;
static {
try {
Properties properties = new Properties();
properties.load(new FileInputStream("JDBC.properties"));
Class.forName(properties.getProperty("driverName"));
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
} catch (Exception e) {
e.printStackTrace();
}
}
}
三、批处理
当使用大量数据时,建议使用批处理。
public class Test2 {
public static void main(String[] args)throws Exception {
ArrayList<User> list = new ArrayList<>();
//集合中放入10000条数据
for (int i = 0; i < 10000; i++) {
User user = new User(i, "处理的数据");
list.add(user);
}
//连接 预编译操作 使用工具类获取连接对象
Connection connection = Utils2.getConnection();
//参数SQL语句
PreparedStatement preparedStatement = connection.prepareStatement("insert into pichuli values(?,?)");
//数据插入数据库
for (User user : list) {
preparedStatement.setInt(1,user.getId());
preparedStatement.setString(2,user.getContent());
//不使用批处理,就会每循环一次list集合,就会执行一次
// preparedStatement.executeUpdate();
//添加批处理,缓存一下
preparedStatement.addBatch();
}
//执行批处理,一次性执行
preparedStatement.executeBatch();
//清空批处理
preparedStatement.clearBatch();
Utils2.close(connection,preparedStatement);
}
}
四、事务
事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
事务的特性:
- 原子性(Atomicity):指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
- 一致性(Consistency):使数据库从一个一致性状态变换到另外一个一致性状态。
- 隔离性(Isolation):多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
- 持久性(Durability):一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
举例:事务的默认状态是自动提交,以下面代码为例。假设前提是转钱操作,语句一是减少钱,语句二是增加钱。执行两条sql语句的中间发生了异常,导致语句一执行,而语句二并没有执行。这显然是不正确的。
public class Test1 {
public static void main(String[] args) throws Exception {
Connection connection = Utils2.getConnection();
String sql1="UPDATE bank SET money=money-1000 WHERE username='lisi'";
String sql2="UPDATE bank SET money=money+1000 WHERE username='zhangsan'";
PreparedStatement preparedStatement1 = connection.prepareStatement(sql1);
PreparedStatement preparedStatement2 = connection.prepareStatement(sql2);
preparedStatement1.executeUpdate();//事务自动提交
//中间存在异常,导致1执行2未执行
System.out.println(1/0);
preparedStatement2.executeUpdate();//事务自动提交
connection.close();
preparedStatement1.close();
preparedStatement2.close();
}
}
那么我们可以通过事务来进行操作,首先关闭事务的自动提交。改为手动提交
public class Test2 {
public static Connection connection;
private static PreparedStatement preparedStatement2;
private static PreparedStatement preparedStatement1;
public static void main(String[] args) {
try {
connection = Utils2.getConnection();
connection.setAutoCommit(false);//默认为true
String sql1 = "UPDATE bank SET money=money-1000 WHERE username='lisi'";
String sql2 = "UPDATE bank SET money=money+1000 WHERE username='zhangsan'";
preparedStatement1 = connection.prepareStatement(sql1);
preparedStatement2 = connection.prepareStatement(sql2);
preparedStatement1.executeUpdate();
//中间存在异常,导致1执行2未执行
System.out.println(1/0);
preparedStatement2.executeUpdate();
} catch ( Exception e) {
e.printStackTrace();
try {
//如果有异常,就回滚事务
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
//提交事务
try {
connection.commit();
connection.close();
preparedStatement1.close();
preparedStatement2.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
System.out.println("操作完成");
}
}