案例:

实现键盘输入数据与数据库中数据校验,如果一致,输出登录成功,否则登录失败

  1. public class JDBCDemo02 {
  2. public static void main(String[] args) {
  3. Connection connection = null;
  4. Statement statement = null;
  5. ResultSet resultSet = null;
  6. Scanner scanner = new Scanner(System.in);
  7. System.out.println("请输入用户名");
  8. String username1 = scanner.next();
  9. System.out.println("请输入密码");
  10. String password1 = scanner.next();
  11. ResourceBundle jdbc = ResourceBundle.getBundle("com/jy/demo/jdbc");
  12. String driver = jdbc.getString("driver");
  13. String url = jdbc.getString("url");
  14. String user = jdbc.getString("user");
  15. String password = jdbc.getString("password");
  16. try {
  17. Class.forName(driver);
  18. connection = DriverManager.getConnection(url,user,password);
  19. statement = connection.createStatement();
  20. //字符串拼接,使得能够查找与键盘输入一致的数据
  21. resultSet = statement.executeQuery("select * from test02 " +
  22. "where username = '"+username1+"'and password = '"+password1+"'");
  23. //resultSet != null判断的是resultSet容器是否存在
  24. //resultSet.next()判断的是容器中是否有元素
  25. if(resultSet != null && resultSet.next()){
  26. System.out.println("登录成功");
  27. }else{
  28. System.out.println("登录失败");
  29. }
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. }finally{
  33. resultSet.close();
  34. statement.close();
  35. connection.close();
  36. }
  37. }
  38. }

案例中的if判断:

resultSet != null判断的是resultSet容器是否存在
resultSet.next()判断的是容器中是否有元素

注意:数据库中的每一张表,都与Java中的一个实体对应,table中的字段对应实体beans中的属性
通过jdbc读取数据,这些数据是以对象的形式被封装在结果集容器中,即resultSet

我们会将读取的数据封装成对象,然后将对象装进集合中(ArrayList/HashMap)
例如上面案例用此方法更合理:

  1. public class JDBCDemo03 {
  2. public static void main(String[] args) {
  3. Connection connection = null;
  4. Statement statement = null;
  5. ResultSet resultSet = null;
  6. boolean flag = false;
  7. Scanner scanner = new Scanner(System.in);
  8. System.out.println("请输入用户名");
  9. String u = scanner.next();
  10. System.out.println("请输入密码");
  11. String p = scanner.next();
  12. ResourceBundle jdbc = ResourceBundle.getBundle("com/jy/demo/jdbc");
  13. String driver = jdbc.getString("driver");
  14. String url = jdbc.getString("url");
  15. String user = jdbc.getString("user");
  16. String password = jdbc.getString("password");
  17. try {
  18. Class.forName(driver);
  19. connection = DriverManager.getConnection(url,user,password);
  20. statement = connection.createStatement();
  21. //字符串拼接,使得能够查找与键盘输入一致的数据
  22. resultSet = statement.executeQuery("select * from test02");
  23. List<User> userList = new ArrayList<>();
  24. if(resultSet != null ){
  25. while (resultSet.next()){
  26. String un = resultSet.getString("username");
  27. String pw = resultSet.getString("password");
  28. User user1 = new User();
  29. user1.setUsername(un);
  30. user1.setPassword(pw);
  31. userList.add(user1);
  32. }
  33. }
  34. for (User user1 : userList) {
  35. if(user1.getUsername().equals(u) && user1.getPassword().equals(p)){
  36. flag = true;
  37. System.out.println("登录成功");
  38. }
  39. }
  40. } catch (Exception e) {
  41. e.printStackTrace();
  42. }
  43. try {
  44. resultSet.close();
  45. } catch (SQLException throwables) {
  46. throwables.printStackTrace();
  47. }
  48. try {
  49. statement.close();
  50. } catch (SQLException throwables) {
  51. throwables.printStackTrace();
  52. }
  53. try {
  54. connection.close();
  55. } catch (SQLException throwables) {
  56. throwables.printStackTrace();
  57. }
  58. }
  59. }


事务

数据库的事务是指一组sql语句组成的数据库逻辑处理单元,在这组的sql操作中,要么全部执行成功,要么全部执行失败。
在jdbc中只要执行了任意一条DML(增删改)语句,都会有一次事务的提交。

事务的原子性: 原子性是指事务的原子性操作,对数据的修改要么全部执行成功,要么全部失败,实现事务的原子性,是基于日志的Redo/Undo机制。

事务的一致性:一致性是指执行事务前后的状态要一致,可以理解为数据一致性。

事务的隔离性:隔离性侧重指事务之间相互隔离,不受影响,这个与事务设置的隔离级别有密切的关系。

事务的持久性:持久性则是指在一个事务提交后,这个事务的状态会被持久化到数据库中,也就是事务提交,对数据的新增、更新将会持久化到书库中。

事务的隔离级别:

Mysql中事务的隔离级别分为四大等级,读未提交(READ UNCOMMITTED)、读提交 (READ COMMITTED)、可重复读 (REPEATABLE READ)、串行化 (SERIALIZABLE)
image.png

事务的一致性
如果出现异常,那么异常之前的语句会被执行,异常之后的不会,这就导致事务的不一致。
例如:A给B转账,那么A的余额会减少,B得余额会增加,可如果A部分语句执行后出现异常,B部分就不会被执行,导致A的余额减少,B的余额没变,这就是事务的不一致。

jdbc默认事务是关闭状态。
所以我们需要在获取连接后开启事务,使得connection.setAutoCommit(true); 默认为true,为true时它会将所有DML语句自动提交,所以需要关闭自动提交,开启手动提交(值设置为false)。手动提交的代码写在程序最后面(try语句块的最后面):调用connection.commit()

当出现异常,我们需要进行回滚,写在catch语句块里:

  1. if(connection != null){
  2. //回滚让事务回到执行前的状态
  3. connection.rollback();
  4. }

悲观锁与乐观锁:

乐观锁:
假设在多线程场景下,多个线程同时对一个数据库的表进行操作,如果多个线程去修改同一条数据,乐观锁的思想是对这种数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁(这使得多个任务可以并行的对数据进行操作),只有到数据提交的时候才通过一种机制来验证数据是否存在冲突(一般实现方式是通过加版本号然后进行版本号的对比方式实现)。
乐观锁可以不用开启事务的支持(采用自动提交的策略)

特点:乐观锁是一种并发类型的锁,其本身不对数据进行加锁通而是通过业务实现锁的功能,不对数据进行加锁就意味着允许多个请求同时访问数据,同时也省掉了对数据加锁和解锁的过程,这种方式因为节省了悲观锁加锁的操作,所以可以一定程度的的提高操作的性能,不过在并发非常高的情况下,会导致大量的请求冲突,冲突导致大部分操作无功而返而浪费资源,所以在高并发的场景下,乐观锁的性能却反而不如悲观锁。

悲观锁:
悲观锁是基于一种悲观的态度类来防止一切数据冲突,它是以一种预防的姿态在修改数据之前把数据锁住,然后再对数据进行读写,在它释放锁之前任何人都不能对其数据进行操作,直到前面一个人把锁释放后下一个人数据加锁才可对数据进行加锁,然后才可以对数据进行操作,一般数据库本身锁的机制都是基于悲观锁的机制实现的。

特点:可以完全保证数据的独占性和正确性,因为每次请求都会先对数据进行加锁, 然后进行数据操作,最后再解锁,而加锁释放锁的过程会造成消耗,所以性能不高。
悲观锁一定要开始事务的支持(采用手动提交)
例如行级锁:
行级锁(又叫排它锁)
使用方式:查询sql语句后加上for update
加上行级锁后,被当前线程查询出的结果集,其他线程无法对结果集进行操作,直到当前事务提交。设置行级锁的同时需要开启事务。
在数据库中执行select … for update ,对数据库中的表或某些行数据进行锁表,在mysql中,如果查询条件带有主键,会锁行数据,如果没有,会锁表

jdbc工具类

封装jdbc工具类的目的是避免重复,如jdbc的六步每次都要写,这是重复的代码。

1、其构造方法被私有化,不能实例化对象,可以直接调用里面的方法。

2、里面的方法为类级别(带static关键字),直接用类名调用。
工具类中的获取数据库连接的方法的异常选择抛出,不在工具类中处理异常。关闭资源方法的异常则可以选择抛出或者捕获。

  1. /*
  2. * 实现JDBC的工具类
  3. */
  4. public class JDBCUtils {
  5. private JDBCUtils(){};
  6. static {
  7. try {
  8. Class.forName("com.mysql.cj.jdbc.Driver");
  9. } catch (ClassNotFoundException e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. public static Connection getConnection() throws SQLException {
  14. return DriverManager.getConnection("jdbc:mysql://localhost:3306/test?" +
  15. "serverTimezone=GMT&characterEncoding=utf-8","root","root");
  16. }
  17. public static void getClose(ResultSet resultSet, Statement statement, Connection connection){
  18. if(resultSet != null ){
  19. try {
  20. resultSet.close();
  21. } catch (SQLException throwables) {
  22. throwables.printStackTrace();
  23. }
  24. }
  25. if(statement != null){
  26. try{
  27. statement.close();
  28. }catch(SQLException ex){}
  29. }
  30. if(connection != null){
  31. try{
  32. connection.close();
  33. }catch(SQLException ex){}
  34. }
  35. }
  36. }