关于java:Java开发之JDBC学习分享

34次阅读

共计 14433 个字符,预计需要花费 37 分钟才能阅读完成。

数据库的操作步骤:

1: 导入数据库的驱动 jar 包 —-> 导入实现类

2: 加载驱动 —–> 通知他我要连贯哪个数据库

3: 取得连贯 ——> 让我和数据库产生关系

4: 取得预处理语句对象 -> 让我 发送 sql 指令到数据库

5: 执行 SQL 命令;—> 预处理语句对象.execute(“SQL 命令行 ”);

6: 开释资源 ——-> 切断我和数据库的分割

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import org.junit.Test;

public class JDBCTest {

@Test
public void testJDBC() throws Exception {

// 1. 加载驱动
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
        Class.forName("com.mysql.jdbc.Driver");// 间接应用此形式:因为底层曾经实现下面的步骤
// 2: 取得连贯 -----> 让 java 程序和数据库建立联系 . 让程序和数据库产生关系
Connection connection = DriverManager.getConnection("jdbc:mysql://IP 地址: 端口号(默认 3306)/ 数据库的名字", "root", "root");
// 3: 取得预处理语句对象 --> 让 java 发送 SQL 语句到数据库
Statement statement = connection.createStatement();
// 4. 执行须要执行的 SQL 语句
statement.execute("create table girl(id int , name varchar(20),age int);");
// 4: 开释资源
statement.close();
connection.close();

}
}

如何找到你要操作的数据集库:

写死 jdbc: 不同的数据库的厂商名://ip 地址: 端口号 / 数据库的库名

操作 mysql 形式 1:jdbc:mysql://localhost:3306/ 数据库的库名

操作 mysql 形式 2:jdbc:mysql://127.0.0.1:3306/ 数据库的库名

如果 操作的数据库在本机 , 并且数据库的端口号是 3306

能够简写 形式:jdbc:mysql:/// 数据库的库名

抽取工具类:DAO 标准的设计

标准 DAO 组件的类名以及包名. 域名 java 培训 倒写. 模块名. 组件名 —-> cn.wolfcode.smis

1. 定义 domain 包以及类名(操作 DAO 都是去操作一张表, 表应该对应着一个 javabean, 取一个非凡的名字 domain)

cn.wolfcode.smis.domain

Student.java

2. 定义 DAO 包以及 DAO 接口.

cn.wolfcode.smis.dao

IxxxDAO—> XXX 示意 domain.

IStudentDAO.java

3. 定义 DAO 实现类的包以及实现类

cn.wolfcode.smis.dao.impl

StudentDAOImpl.java

4. 测试类

cn.wolfcode.smis.test

StudentDAOImplTest.java

5. 工具类

cn.wolfcode.smis.util

JDBCUtil.java


标准的开发步骤 — 实现保留操作:

1. 创立数据库表.

2. 依据数据库表创立对应的 domain 包和类.

留神: 表的列的名字和类型与类的属性名和类型要一一匹配.

3. 通过 domain 来创立 DAO 包以及接口.

4. 依据接口生成实现类.

5. 依据接口 / 实现类生成测试类. 实现测试的代码.

6. 实现一个办法, 测试一个办法, 测试通过, 再写写一个办法.

预编译语句对象 -PreparedStatement:

预编译语句对象 -PreparedStatement:

PreparedStatement 是动态语句对象的子类. 所以, 父类中的所有的办法都能够调用到. 然而个别咱们不会调用. 调用本人特有的办法.

预编译语句对象是通过带有占位符的 SQL 模板来创建对象的.

SQL 模板: 带有占位符? 的 SQL. 雷同的操作,SQL 语句其实是一样的, 仅仅是参数不一样而已.

参数确定不了, 应用占位符? 来示意.

常见 API:

  1. 获取预编译语句对象.

应用数据库连贯对象:

PreparedStatement prepareStatement(String sql); 传递的是 SQL 模板, 有占位符

2. 在执行 SQL 之前, 肯定要给占位符设置值.

void setObject(int parameterIndex,Object x):

parameterIndex: 参数索引, 在 JDBC 中, 索引是从 1 开始的.

3. 执行 SQL 命令:

留神, 不要调用父类带有参数的办法.

DQL 操作:增删改

在 JDK 中, 把如何解析后果集的操作, 封装到了一个对象中.ResultSet.

需要:

1. 查问 id 为 10 的学生信息

2. 查问所有的学生信息.

ResultSet 对象:

把如何解析后果集的过程封装成了对象.

boolean next(): 往下挪动一行, 如果返回 true, 示意有数据, 否则没有数据. 要获取数据, 肯定要调用 next 办法.

Object getObject(int columnIndex): 依据列的地位来取数据. 索引从 1 开始.

Object getObject(String columnLabel): 依据列名来获取数据

优化:

1. 连贯数据库四因素反复写很多遍(抽取到公共的地位, 只写一遍).

2.Class.forName 加载字节码, 字节码只会加载一次(应用动态代码块去加载注册驱动).

3. 硬编码.(连贯数据库四因素).

4. 代码反复(DML 操作, DQL 操作).

5. 每次获取数据库连贯对象, 用完间接就敞开了. 节约.

代码:

配置文件:

符号 javaBean 的 Student 类:

DAO 接口的定义:

接口的测试类:

JDBC 的工具类:

package cn.wolfcode.smis.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

// 工具类
public class MyJDBCUtil {
// 私有化结构器
private MyJDBCUtil() {
}

static Properties p = new Properties();
// 动态代码块
static {

try {
  // 加载配置文件
  p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("pd.properties"));
  // 加载驱动器
  Class.forName(p.getProperty("driverClassName"));
} catch (Exception e) {e.printStackTrace();
}

}

// 取得连贯对象
public static Connection getConnection() {

Connection connection = null;
try {connection = DriverManager.getConnection(p.getProperty("url"), p.getProperty("username"),
      p.getProperty("password"));
} catch (Exception e) {e.printStackTrace();
}
return connection;

}

// 敞开资源
public static void close(Connection connection, Statement statement) {

try {if (statement != null) {statement.close();
  }
} catch (SQLException e) {e.printStackTrace();
}
try {if (connection != null) {connection.close();
  }
} catch (SQLException e) {e.printStackTrace();
}

}

// 重载敞开资源
public static void close(Connection connection, Statement statement, ResultSet resultSet) {

try {if (statement != null) {statement.close();
  }
} catch (SQLException e) {e.printStackTrace();
}
try {if (connection != null) {connection.close();
  }
} catch (SQLException e) {e.printStackTrace();
}
try {if (resultSet != null) {resultSet.close();
  }
} catch (SQLException e) {e.printStackTrace();
}

}

}

接口的实现类:

package cn.wolfcode.smis.dao.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import cn.wolfcode.smis.dao.IStudentDAO;
import cn.wolfcode.smis.domain.Student;
import cn.wolfcode.smis.util.MyJDBCUtil;

public class StudentDAOImpl implements IStudentDAO {

@Override
public void insert(Student student) {

// 取得连贯对象
Connection connection = MyJDBCUtil.getConnection();
// 取得预编译对象
PreparedStatement statement = null;
try {statement = connection.prepareStatement("insert into student(id,name,age)  values(?,?,?)");
  // 设置站位符
  statement.setObject(1, student.getId());
  statement.setObject(2, student.getName());
  statement.setObject(3, student.getAge());
  // 执行
  statement.execute();} catch (Exception e) {e.printStackTrace();
} finally {
  // 敞开资源
  MyJDBCUtil.close(connection, statement);
}

}

@Override
public void delete(Long id) {

// 取得连贯对象
Connection connection = MyJDBCUtil.getConnection();
// 取得预编译对象
PreparedStatement statement = null;
try {statement = connection.prepareStatement("delete from student where id = ?");
  // 设置站位符
  statement.setObject(1, id);
  // 执行
  statement.execute();} catch (Exception e) {e.printStackTrace();
} finally {
  // 敞开资源
  MyJDBCUtil.close(connection, statement);
}

}

@Override
public void update(Student student) {

// 取得连贯对象
Connection connection = MyJDBCUtil.getConnection();
// 取得预编译对象
PreparedStatement statement = null;
try {statement = connection.prepareStatement("update student set name = ? ,age = ? where id = ?");
  // 设置站位符
  statement.setObject(1, student.getName());
  statement.setObject(2, student.getAge());
  statement.setObject(3, student.getId());
  // 执行更新办法
  statement.executeUpdate();} catch (Exception e) {e.printStackTrace();
} finally {
  // 敞开资源
  MyJDBCUtil.close(connection, statement);
}

}

@Override
public Student selectOne(Long id) {

// 取得连贯对象
Connection connection = MyJDBCUtil.getConnection();
// 取得预编译对象
PreparedStatement statement = null;
Student student = null;
try {statement = connection.prepareStatement("select * from student where id = ?");
  // 设置站位符
  statement.setObject(1, id);
  // 执行查问,返回后果集
  ResultSet resultSet = statement.executeQuery();
  if (resultSet.next()) {// 后果集中有元素
    // 失去后果集中的元素 ----> 后果集中是一个个字段对应的值,听过字段名取得值,而后创立个对象返回
    Object name = resultSet.getObject("name");
    Object age = resultSet.getObject("age");
    // 创建对象
    student = new Student(id, (String) name, (Integer) age);
  }
} catch (Exception e) {e.printStackTrace();
} finally {
  // 敞开资源
  MyJDBCUtil.close(connection, statement);
}
return student;

}

@Override
public List<Student> selectAll() {

// 创立 List
List<Student> list = new ArrayList<>();
// 取得连贯对象
Connection connection = MyJDBCUtil.getConnection();
// 取得预编译对象
PreparedStatement statement = null;
try {statement = connection.prepareStatement("select * from student");
  // 执行查问,返回后果集
  ResultSet resultSet = statement.executeQuery();
  // if (resultSet.next()) {// 后果集中有元素
  // 用 while 循环将后果集中所有的元素取出来
  while (resultSet.next()) {
    // 失去后果集中的元素 ----> 后果集中是一个个字段对应的值,听过字段名取得值,而后创立个对象返回
    Object id = resultSet.getObject("id");
    Object name = resultSet.getObject("name");
    Object age = resultSet.getObject("age");
    // 创建对象并将对象增加进 list 中
    list.add(new Student((Long) id, (String) name, (Integer) age));
  }
} catch (Exception e) {e.printStackTrace();
} finally {
  // 敞开资源
  MyJDBCUtil.close(connection, statement);
}
return list;

}

}

上述代码仍然有反复:应用模板办法可再进行优化。

注入问题:经典案例登录平安问题;

@Test
public void testLogin() throws Exception {

// 模仿账户的登录, 了解 statement 和 preparestatement 的区别
String username = "will";
// String password = "123456";
// 注入问题
String password = "'or'1'='1";
// loginByStatement(username, password);
// loginByPrepareStatement(username, password);
loginByPrepareStatement1(username, password);

}

// 没有应用预处理语句对象: 不平安 String password = “‘ or ‘1’=’1”; 时能够屏蔽 SQL 语句的语义。导致胜利登录
private void loginByStatement(String username, String password) throws Exception {

// 验证 username 和 password 在数据库中的表 是否存在这一条数据
Connection connection = JDBCUtil.getConnection();
Statement statement = connection.createStatement();
String sql = "select * from user where username ='" + username + "'and password ='" + password + "'";
ResultSet resultSet = statement.executeQuery(sql);
if (resultSet.next()) {System.out.println("登录胜利");
} else {System.out.println("账户或者明码谬误, 登录失败");
}
JDBCUtil.close(connection, statement, resultSet);

}

// 模仿登录案例 —> 应用预处理对象:平安
private void loginByPrepareStatement1(String username, String password) throws Exception {

// 把参数和数据库中的客户信息比照,雷同能力登录胜利
// 1 连贯数据库
Connection connection = JDBCUtil.getConnection();
// 2 取得预处理语句对象
PreparedStatement statement = connection
    .prepareStatement("select * from user where username = ? and password = ?");
// 2.1 设置占位符
statement.setObject(1, username);
statement.setObject(2, password);
// 3 查问后果
ResultSet resultSet = statement.executeQuery();
// 判断数据库中是否有这条用户数据
if (resultSet.next()) {System.out.println("登录胜利!");
} else {System.out.println("账户不存在!登录失败!!");
}
// 敞开资源
JDBCUtil.close(connection, statement, resultSet);

}

事务:经典案例,转账平安问题。

冀望后果: 如果转账胜利, 那么数据发生变化. 如果转账失败, 那么账户余额不应该扭转, 应该还是转账之前的状态.

下面这种操作, 就是数据库中的事务操作.

事务(Transaction, 简写为 tx):

在数据库中, 所谓事务是指一组逻辑操作单元, 使数据从一种状态变换到另一种状态。

为确保数据库中数据的一致性, 数据的操纵该当是离散的成组的逻辑单元:

当每个逻辑操作单元全副实现时, 数据的一致性能够放弃,

而当这个单元中的一部分操作失败, 整个事务应全副视为谬误, 所有从起始点当前的操作应全副回退到开始状态。

事务的操作:

先定义开始一个事务, 而后对数据作批改操作, 这时如果提交(commit), 这些批改就永恒地保留下来, 如果回退(rollback), 数据库管理系统将放弃您所作的所有批改而回到开始事务时的状态。

事务:指形成单个逻辑工作单元的操作汇合

事务处理:保障所有事务都作为一个工作单元来执行,即便呈现了故障,都不能扭转这种执行形式。当在一个事务中执行多个操作时,要么所有的事务都被提交 (commit),要么整个事务回滚(rollback) 到最后状态

说白了:

1. 须要把多个操作放在一起作为一个整体.

2. 全副胜利, 才叫胜利, 否则一个失败, 全副都失败.

事务的 ACID 属性:

  1. 原子性(Atomicity): 原子在化学中, 是最小单位, 不能够再宰割了.

原子性是指事务是一个不可分割的工作单位,事务中的操作要么都产生,要么都不产生。

  1. 一致性(Consistency): 保证数据的完整性.

事务必须使数据库从一个一致性状态变换到另外一个一致性状态。(数据不被毁坏)

  1. 隔离性(Isolation):

事务的隔离性是指一个事务的执行不能被其余事务烦扰,即一个事务外部的操作及应用的数据对并发的其余事务是隔离的,并发执行的各个事务之间不能相互烦扰。

  1. 持久性(Durability):

持久性是指一个事务一旦被提交,它对数据库中数据的扭转就是永久性的,接下来的其余操作和数据库故障不应该对其有任何影响

事务相干的细节:

1): 默认状况下, 事务在执行完 DML 操作就主动提交.

2): 查问操作, 其实是不须要事务的. 然而, 个别的, 咱们在开发中都把查问放入事务中.

3): 开发中, 代码完全正确, 没有异样, 然而就是数据库中数据不变.

意识: 可能没有提交事务.

4): 在 MySQL 中, 只有 InnoDB 存储引擎反对事务, 反对外键,MyISAM 不反对事务.

5): 当前事务咱们不应该在 DAO 层解决, 应该在 service 层管制(提出).

6): 事务在解说框架和我的项目的时候都会再讲.

事务操作, 在一次连贯中进行屡次操作. 须要将多个操作放在一个整体中, 就须要应用事务.

操作模板:

try{
// 手动开启一个事务
conn.setAutoCommit(false);
操作 1 …
操作 2 …
操作 N …
// 全副胜利, 提交事务
conn.commit();
} catch(Exception e){
// 异样代码解决
// 如果失败, 须要回滚
conn.rollback();
} finally{
// 敞开资源
}

留神:

只有在调用了 commit 办法之后, 数据库才会真正的发生变化. 全副胜利, 调用 commit 办法. 如果失败了肯定要调用 rollback 办法.

起因: 在开启事务的时候, 就有一个事务锁的存在. 必须要调用 commit 或者 rollback 才能够开释.

@Test
public void testTransaction() {

// 模仿转账的成果 西门吹雪 转账给 菠萝吹雪 1000 钱。如果转账过程中出现异常,则金额不应该产生扭转。// 1: 先判断 西门吹雪 有没有钱
Connection connection = JDBCUtil.getConnection();
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
  // 勾销主动提交,让残缺的事物执行完后再进行收动提交
  connection.setAutoCommit(false);
  statement = connection.prepareStatement("select * from account where name =' 西门吹雪 'and balance >= 1000");
  resultSet = statement.executeQuery();
  // 只有金额足够能力转账
  if (resultSet.next()) {System.out.println("开始转账");
    // 西门吹雪的 钱 缩小 1000
    statement.executeUpdate("update account set balance = balance - 1000 where name =' 西门吹雪 '");
    // int ret = 10 / 0;// 制作异样 ----> 如果转账过程中出现异常,则金额不应该产生扭转。// 菠萝吹雪金莲的钱 减少 1000
    statement.executeUpdate("update account set balance = balance + 1000 where name =' 菠萝吹雪 '");


    // 手动提交事务
    connection.commit();} else {System.out.println("没钱!!!, 我的二叔是公安局长");
  }
} catch (Exception e) {System.out.println("转账异常中断!");
  e.printStackTrace();
  // 回退:出现异常时,将事务回退到最开始的状态。----> 只有呈现异常中断时才须要回退,所以放到 catch 语句中
  try {connection.rollback();
  } catch (SQLException e1) {e1.printStackTrace();
  }
} finally {
  // 敞开资源:无论如何最初都要敞开资源
  JDBCUtil.close(connection, statement, resultSet);
}

}

取得主动生成的主键:取得注册后信息

1. 设置一个标记, 给数据库说, 我要主动生成的主键.

在获取预编译语句对象的时候, 传递要获取的标记.

Statement.RETURN_GENERATED_KEYS .

2. 获取主动生成的主键.

// 模仿注册后显示 id 和注册时的信息
@Test
public void testInsert() throws Exception {

// 注册时的输出的信息
String username = "天河首富";
String password = "lh0030";
// 取得连贯对象
Connection connection = JDBCUtil.getConnection();
String sql = "insert into user (username,password) values(?,?)";
// 取得预处理语句对象
PreparedStatement statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);// 取得主动生成的主键
// 设置占位符的值
statement.setObject(1, username);
statement.setObject(2, password);
// 执行更新操作
statement.executeUpdate();
// 把主动生成的主键放到了一个后果集对象中
ResultSet keys = statement.getGeneratedKeys();
// 如果插入胜利就返回主键和插入的信息
if (keys.next()) {
  // 获取主动生成的主键
  long id = (long) keys.getObject(1);
  System.out.println(id + "" + username +"  " + password);
}
// 敞开资源
JDBCUtil.close(connection, statement, keys);

}

数据库连接池:

目前咱们操作数据库的形式:

1. 获取数据库连贯对象.

2. 执行 SQL 命令

3. 解析后果集.

4. 敞开资源(后果集对象, 预编译语句对象, 数据库连贯对象)

执行完了一个 SQL 命令之后, 就会敞开数据库连贯对象. 问题就出在这里.

为什么必须应用数据库连接池:

一般的 JDBC 数据库连贯 (Connection 对象) 应用 DriverManager 来获取,每次向数据库建设连贯的时候都要将 Connection 加载到内存中,再验证用户名和明码(得破费 0.05s~1s 的工夫), 数据库的连贯是比拟低廉的(创立的老本比拟大)。

须要数据库连贯的时候,就向数据库要求一个,执行实现后再断开连接。这样的形式将会耗费大量的资源和工夫。

数据库的连贯资源并没有失去很好的反复利用. 若同时有几百人甚至几千人在线,频繁的进行数据库连贯操作将占用很多的系统资源,重大的甚至会造成服务器的解体。

对于每一次数据库连贯,应用完后都得断开。否则,如果程序出现异常而未能敞开,将会导致数据库系统中的内存透露,最终将导致重启数据库。

这种开发不能管制被创立的连贯对象数,系统资源会被毫无顾及的调配进来,如连贯过多,也可能导致内存透露,服务器解体.

说白了:

以后的操作数据库的形式是, 破费了大量的资源和工夫创立的十分低廉的数据库连贯对象, 应用一次之后就敞开了. 次要存在的问题是, 没有充沛应用到数据库连贯对象. 应用连接池来解决这个问题.

池化技术: 治理资源对象, 达到最高的效率.

池化技术可能缩小资源对象的创立次数,进步程序的性能,特地是在高并发下这种进步更加显著。应用池化技术缓存的资源对象有如下独特特点:

1,对象创立工夫长;

2,对象创立须要大量资源;3,对象创立后可被重复使用。比方 thread,connection 等对象都具备下面的几个独特特点。

在 Java 中, 连接池应用 javax.sql.DataSource 接口来示意连接池.

DataSource(数据源)和连接池 (Connection Pool) 是同一个.

留神:DataSource 仅仅只是一个接口, 由各大服务器厂商来实现(Tomcat,JBoss).

罕用的 DataSource 的实现:

DBCP: Spring 框架举荐的

druid: 阿里巴巴的连接池 (号称 Java 语言中性能最好的连接池) 德鲁伊.

==============================================================

应用连接池和不应用连接池的区别在哪里?

如何获取 Connection 对象:

未应用连接池:

Connection conn = DriverManager.getConnection(url,username,password);

应用了连接池:

datasource 对象.getConnection();// 返回的连贯对象曾经是一个性能加强的连贯对象了.

只有获取了 Connection 对象, 接下来的操作和以前是截然不同的.

关键在于: 如何创立 DataSource 对象.

如何开释 Connection 对象(Connection 对象.close()):

未应用连接池: 是和数据库服务器断开.

应用了连接池: 还给连接池.

应用数据库连接池创立 JDBC 的工具类:

形式 1:

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

//JDBC 工具类
public class JDBCUtil {
// 私有化结构器
private JDBCUtil() {
}

static Properties p = new Properties();
static {

try {p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
  Class.forName(p.getProperty("driverClassName"));


} catch (IOException e) {System.err.println("你的配置文件加载不到");
  e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();
}

}

// 取得数据库的连贯对象
public static Connection getConnection() {

// 取得 资源文件夹中的 db.properties 文件中数据库连贯信息
Connection connection = null;
try {connection = DriverManager.getConnection(p.getProperty("url"), p.getProperty("username"),
      p.getProperty("password"));
} catch (SQLException e) {e.printStackTrace();
}
return connection;

}
}

形式 2:

import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSourceFactory;// 德鲁伊的 jar 包

//JDBC 工具类
public class JDBCUtil {
// 私有化结构器
private JDBCUtil() {
}

static DataSource source = null;
static {

try {Properties p = new Properties();
  p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
  // 这种形式创建对象
  source = DruidDataSourceFactory.createDataSource(p);
} catch (IOException e) {e.printStackTrace();
} catch (Exception e) {e.printStackTrace();
}

}

// 取得数据库的连贯对象
public static Connection getConnection() {

Connection connection = null;
try {connection = source.getConnection();
} catch (SQLException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();}
return connection;

}
// 省略关流操作,关流没变
}

正文完
 0