JavaWeb笔记之JavaWeb JDBC

//Author 流云

//Version 1.0
   

一. 引言

1.1 如何操作数据库
使用客户端工具访问数据库,需要手工建立连接,输入用户名和密码登录,编写 SQL 语句,点击执行,查看操作结果(结果集或受影响行数)。

   
1.2 实际开发中,不会采用数据库客户端操作
在实际开发中,当用户的数据发生改变时,不可能通过客户端操作执行 SQL 语句,因为操作量过大,无法保证效率和正确性。
    

二. JDBC

2.1 介绍
JDBC(Java Database Connectivity) Java 连接数据库的规范(标准),可以使用 Java 语言连接数据库完成 CRUD 操作。
     
2.2 核心思想
Java 中定义了访问数据库的接口,可以为多种关系型数据库提供统一的访问方式。由数据库厂商提供驱动实现类(Driver 数据库驱动)。

  
2.3 驱动包
mysql-connector-java-5.1.X 适用于 5.X 版本
mysql-connector-java-8.0.X 适用于 8.X版本
  
2.4 常用类
JDBC 是由多个接口和类进行功能实现。

    
2.5 环境搭建
在项目下新建 lib 文件夹,用于存放 jar 文件。
将 mysql 驱动mysql-connector-java-5.1.X复制到项目的 lib 文件夹中。
选中 lib 文件夹右键 Add as Libraay,点击 OK。
    

三. 开发步骤

3.1 注册驱动
使用 Class.forName("com.mysql.jdbc.Driver");手动加载字节码文件到 JVM 中。
Class.forName("com.mysql.jdbc.Driver");//加载驱动
  
3.2 连接数据库
通过 DriverManager.getConnection(url,user,password) 获取数据库连接对象。
        URL:jdbc:mysql://localhost:3306/database
        username:root
        password:1234
Connection conn = 
DriverManager.getConnection("jdbc:mysql://localhost:3306/database?
useUnicode=true&characterEncoding=utf8", "root","1234");
  
URL(Uniform Resource Locator) 统一资源定位符:由协议、IP、端口、SID(程序实例名称)组成。
   
3.3 获取执行对象
通过 Connection 对象获得 Statement 对象,用于对数据库进行通用访问。
Statement statement = conn.createStatement();
  
3.4 执行SQL 语句
执行 SQL 语句并接收执行结果。
String sql ="INSERT INTO t_jobs(JOB_ID,JOB_TITLE,MIN_SALARY,MAX_SALARY)
VALUES('JAVA_Le','JAVA_Lecturer',4000,10000);";

int result = statement.executeUpdate(sql);//执行SQL语句并接收结果
   
注意:在编写 DML 语句时,一定要注意字符串参数的符号是单引号 '值'。
DML 语句:增删改时,返回受影响行数(int 类型)。
DQL 语句:查询时,返回结果数据(ResultSet 结果集)。
    
3.5 处理结果
接受处理操作结果。
if(result == 1){
    System.out.println("Success");
}
   
受影响行数:逻辑判断、方法返回。
查询结果集:迭代、依次获取。
  
3.6 释放资源
遵循先开后关原则,释放所使用到的资源对象。
statement.close();
conn.close();
   
3.7 综合案例
整合以上核心六步,实现向数据库表中插入一条数据。
package com.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class DeleteJdbc {
    public static void main(String[] args) throws Exception{
        //1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.获得连接对象
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb","root","1234");
        //3.获得执行SQL的对象
        Statement statement = connection.createStatement();
        //4.执行SQL语句,并接收结果
        int result = statement.executeUpdate("delete from t_jobs where job_id = 'H5_mgr';");
        //5.处理结果
        if(result==1){
            System.out.println("删除成功!");
        }else{
            System.out.println("删除失败!");
        }
        //6.释放资源
        statement.close();
        connection.close();
    }
}
  

四. ResultSet

在执行查询 SQL 后,存放查询到的结果集数据。
  
4.1 接收结果集
ResultSet rs = statement.executeQuery(sql);
ResultSet rs= statement.executeQuery("SELECT * FROM t_employees;");
  
4.2 遍历结果集
ResultSet 以表(table)结构进行临时结果的存储,需要通过 JDBC API 将其中数据进行依次获取。
        数据行指针:初始位置在第一行数据前,每调用一次 boolean next()方法ResultSet 的指针向下移动一行,结果为 true,表示当前行有数据。
        rs.getXxx(整数);代表根据列的编号顺序获得,从 1 开始。
        rs.getXxx("列名");代表根据列名获得。
boolean next() throws SQLException //判断 rs 结果集中下一行是否存在数据
  
4.2.1 遍历方法
int getInt(int columnIndex) throws SQLException //获得当前行第N列的int值
int getInt(String columnLabel) throws SQLException //获得当前行columnLabel列的int值

double getDouble(int columnIndex) throws SQLException //获得当前行第N列的double值
double getDouble(String columnLabel) throws SQLException //获得当前行columnLabel列的double值

String getString(int columnIndex) throws SQLException //获得当前行第N列的String值
String getString(String columnLabel) throws SQLException //获得当前行columnLabel列的String值

......
  
注意:列的编号从 1 开始。
  
4.3 综合案例
对 t_jobs 表中的所有数据进行遍历。
    
4.3.1 根据列名
package com.www.test;

import java.sql.*;

public class JobsQuery {
    public static void main(String[] args) {
        //1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取数据库连接对象
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/product", "root", "1234");
        //3.获取发送 sql 语句对象
        Statement statement = connection.createStatement();
        //4.执行 SQL 语句并接收结果集
        ResultSet resultSet = statement.executeQuery("select * from t_jobs");
        //5 处理结果集
        while(resultSet.next()){
            //5.1有数据,依据列名获取数据
            String job_id = resultSet.getString("job_id");
            String job_title = resultSet.getString("job_title");
            int min_salary = resultSet.getInt("min_salary");
            int max_salary = resultSet.getInt("max_salary");
            System.out.println(job_id+"\t"+job_title+"\t"+min_salary+"\t"+max_salary);
        }
        //6.释放资源
        rs.close();
        statement.close();
        connection.close();
    }
}
    
4.3.2 根据编号
//。。。。与上无异
while(resultSet.next()){
    //5.2有数据,依据列名获取数据
    String job_id = resultSet.getString(1);
    String job_title = resultSet.getString(2);
    int min_salary = resultSet.getInt(3);
    int max_salary = resultSet.getInt(4);
    System.out.println(job_id+"\t"+job_title+"\t"+min_salary+"\t"+max_salary);
}
//释放资源
  

五. 常见错误

java.lang.ClassNotFoundException:找不到类(类名书写错误、没有导入jar包)。
java.sql.SQLException:与sql语句相关的错误 (约束错误、表名列名书写错误) 建议:在客户端工具中测试SQL语句之后再粘贴在代码中。
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 原因:列值Sting类型没有加单引号。
Duplicate entry '1' for key 'PRIMARY' 原因,主键值已存在或混乱,更改主键值或清空表。
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'password' in 原因:可能输入的值的类型不对,确定是否插入的元素时对应的值的类型正确。
    

六. 登录案例

6.1 创建表
创建一张用户表 User
        id ,主键、自动增长。
        用户名,字符串类型,唯一、非空
        密码,字符串类型,非空
        手机号码,字符串类型
插入 2 条测试语句
   
6.2 实现登录
通过控制台用户输入用户名和密码。
用户输入的用户名和密码作为条件,编写查询 SQL 语句。
如果该用户存在,提示登录成功,反之提示失败。
    

七. SQL注入

7.1 介绍
用户输入的数据中有 SQL 关键字或语法并且参与了 SQL 语句的编译,导致 SQL 语句编译后的条件含义为 true,一直得到正确的结果。这种现象称为 SQL 注入。
  
7.2 方案
由于编写的 SQL 语句是在用户输入数据,整合后再进行编译。所以为了避免 SQL 注入的问题,我们要使 SQL 语句在用户输入数据前就已进行编译成完整的 SQL 语句,再进行填充数据。
  

八. PreparedStatement

PreparedStatement 继承了 Statement 接口,执行 SQL 语句的方法无异。
  
8.1 应用
作用:
预编译SQL 语句,效率高。
安全,避免SQL注入 。
可以动态的填充数据,执行多个同构的 SQL 语句。
  
8.1.1 参数标记
//1.预编译 SQL 语句
PreparedStatement pstmt = conn.prepareStatement("select * from user where username=? and password=?");
  
注意:JDBC中的所有参数都由 ?符号占位,这被称为参数标记。在执行SQL语句之前,必须为每个参数提供值。 
    
8.1.2 动态参数绑定
pstmt.setXxx(下标,值) 参数下标从 1 开始,为指定参数下标绑定值。
//1.预编译 SQL 语句
PreparedStatement pstmt = conn.prepareStatement("select * from user where username=? and password=?");

//2.为参数下标赋值
pstmt.setString(1,username);
pstmt.setString(2,password);
  

九. 封装工具类

在实际JDBC的使用中,存在着大量的重复代码:例如连接数据库、关闭数据库等这些操作!
我们需要把传统的JDBC代码进行重构,抽取出通用的JDBC工具类!以后连接任何数据库、释放资源都可以使用这个工具类。

  
9.1 重用性方案
封装获取连接、释放资源两个方法。
        提供public static Connection getConnection(){}方法。
        提供public static void closeAll(Connection conn , Statement sm , ResultSet rs){}方法。
  
9.1.1 实现
package com.jdbc;

import java.sql.*;

/**
* 重用性方案
* 获取连接
* 释放资源
*/
 
public class DBUtils {
    static {//类加载,执行一次!
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    //1.获取连接
    public static Connection getConnection() {
        Connection connection = null;
        try {
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb", "root","1234");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    //2.释放资源
    public static void closeAll(Connection connection, Statement statement, ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
  
9.2 跨平台方案
定义public static final Properties prop = new Properties(); //读取配置文件的Map。
定义static{
        //首次使用工具类时,加载驱动
InputStream is = JDBCUtil.class.getResourceAsStream("路径"); //通过复用本类自带流,读取jdbc.properties配置文件。classPath = bin
prop.load(is); //通过prop对象将流中的配置信息分割成键值对。
String driverName = prop.getProperty("driver"); //通过driverName的键获取对应的值(com.mysql.jdbc.Driver)
Class.forName(driverName); //加载驱动。
}
    
9.2.1 实现
在src 目录下新建 db.properties 文件。
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb3
user=root
password=123456
    
工具类的封装。
package com.jdbc2;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class DBUtils {
    private static final Properties PROPERTIES = new Properties();//存储配置文件的map
    static {
        InputStream is = DBUtils.class.getResourceAsStream("/db.properties");
        try {
            PROPERTIES.load(is);//通过流,将配置文件内容加载到properties集合
            Class.forName(PROPERTIES.getProperty("driver"));
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnection() {
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(PROPERTIES.getProperty("url"),
            PROPERTIES.getProperty("username"), PROPERTIES.getProperty("password"));
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
    public static void closeAll(Connection connection, Statement statement, ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
   

十. ORM

ORM(Object Relational Mapping)。
从数据库查询到的结果集(ResultSet)在进行遍历时,逐行遍历,取出的都是零散的数据。在实际应用开发中,我们需要将零散的数据进行封装整理。
   
10.1 实体类
一行数据中,多个零散的数据进行整理。
通过entity的规则对表中的数据进行对象的封装。
表名=类名;列名=属性名;提供各个属性的get、set方法。
提供无参构造方法、(视情况添加有参构造)。
      
10.1.1 ORM应用
entity实体类
package com.www.test;

public class T_Jobs {
    private String job_id;
    private String job_title;
    private int min_salary;
    private int max_salary;
    @Override
    public String toString() {
        return "T_Jobs{" +
        "job_id='" + job_id + '\'' +
        ", job_title='" + job_title + '\'' +
        ", min_salary=" + min_salary +
        ", max_salary=" + max_salary +
        '}';
    }
    public String getJob_id() {
        return job_id;
    }
    public void setJob_id(String job_id) {
        this.job_id = job_id;
    }
    public String getJob_title() {
        return job_title;
    }
    public void setJob_title(String job_title) {
        this.job_title = job_title;
    }
    public int getMin_salary() {
        return min_salary;
    }
    public void setMin_salary(int min_salary) {
        this.min_salary = min_salary;
    }
    public int getMax_salary() {
        return max_salary;
    }
    public void setMax_salary(int max_salary) {
        this.max_salary = max_salary;
    }
    public T_Jobs() {
    }
    public T_Jobs(String job_id, String job_title, int min_salary, int max_salary) {
        this.job_id = job_id;
        this.job_title = job_title;
        this.min_salary = min_salary;
        this.max_salary = max_salary;
    }
}
   
查询结果封装
package com.www.test;

import java.sql.*;

public class JobsQuery {
    public static void main(String[] args) {
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        String URL = "jdbc:mysql://localhost:3306/product";
        String user = "root";
        String password = "1234";
        try {
            //1.加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取数据库连接对象
            connection = DriverManager.getConnection(URL, user, password);
            //3.编写 SQL 语句
            String sql = "select * from t_jobs";
            //4.获取发送 sql 语句对象
            statement = connection.createStatement();
            //5.执行 SQL 语句并接收结果集
            resultSet = statement.executeQuery(sql);
            //5.1使用 while 循环判断下一行是否有数据
            while(resultSet.next()){
                //5.2有数据,依据列名获取数据
                String job_id = resultSet.getString(1);
                String job_title = resultSet.getString(2);
                int min_salary = resultSet.getInt(3);
                int max_salary = resultSet.getInt(4);
                //5.3 创建实体类对象
                T_Jobs t_jobs = new T_Jobs();
                //5.4 每列数据对应属性进行赋值
                t_jobs.setJob_id(job_id);
                t_jobs.setJob_title(job_title);
                t_jobs.setMin_salary(min_salary);
                t_jobs.setMax_salary(max_salary);
                System.out.println(t_jobs);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                //6.释放资源
                if (resultSet != null) {
                    resultSet.close();
                }
                if (statement != null) {
                    statement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
   

十一. 数据访问对象

DAO 实现了业务逻辑与数据库访问相分离。
        对同一张表的所有操作封装在XxxDaoImpl对象中。
        根据增删改查的不同功能实现具体的方法(insert、update、delete、select、 selectAll)。

  
11.1 创建数据库
创建一张表 Person,有以下列:
        id:int,主键,自动增长
        name:varchar(20) 非空
        age:int 非空
        bornDate:Date
        email:字符串
        address:字符串
   
11.2 封装实体类
创建entity实体类 Person,编写属性私有化,构造方法,get/set 方法。
  
11.3 编写 DaoImpl 类
编写 DaoImpl 类,提供增删改查方法,使用 JDBC 开发步骤,完成功能。
package com.person;

import sun.awt.image.DataBufferNative;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
* 增、删、改、查单个、查所有
* 只做数据库访问操作!不参与逻辑判断
* 数据库一张表的访问的操作复用!
*/

public class PersonDaoImpl {
    //新增
    public int insert(Person person){
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        String sql = "insert into person(name,age,borndate,email,address) values(?,?,?,?,?);";
        try {
            connection = DBUtils.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1,person.getName());
            preparedStatement.setInt(2,person.getAge());
            preparedStatement.setDate(3,null);
            preparedStatement.setString(4,person.getEmail());
            preparedStatement.setString(5,person.getAddress());
            int result = preparedStatement.executeUpdate();
            return result;
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtils.closeAll(connection,preparedStatement,null);
        }
        return 0;
    }
    //修改
    public int update(Person person){
        Connection connection =null;
        PreparedStatement preparedStatement = null;
        String sql = "update person set
        name=?,age=?,borndate=?,email=?,address=? where id = ?";
        try {
            connection = DBUtils.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1,person.getName());
            preparedStatement.setInt(2,person.getAge());
            preparedStatement.setDate(3,null);
            preparedStatement.setString(4,person.getEmail());
            preparedStatement.setString(5,person.getAddress());
            preparedStatement.setInt(6,person.getId());
            int result = preparedStatement.executeUpdate();
            return result;
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtils.closeAll(connection,preparedStatement,null);
        }
        return 0;
    }
    //删除
    public int delete(int id){
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        String sql = "delete from person where id= ?;";
        connection = DBUtils.getConnection();
        try {
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1,id);
            int result = preparedStatement.executeUpdate();
            return result;
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtils.closeAll(connection,preparedStatement,null);
        }
        return 0;
    }
    //查单个
    public Person select(int id){
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        String sql = "select * from person where id = ?;";
        Person person = null;
        try {
            connection = DBUtils.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1,id);
            resultSet = preparedStatement.executeQuery();
            if(resultSet.next()){
                person = new Person();
                int pid= resultSet.getInt("id");
                String name = resultSet.getString("name");
                int age = resultSet.getInt("age");
                Date bornDate = resultSet.getDate("borndate");
                String email = resultSet.getString("email");
                String address = resultSet.getString("address");
                person.setId(pid);
                person.setName(name);
                person.setAge(age);
                person.setBornDate(bornDate);
                person.setEmail(email);
                person.setAddress(address);
            }
            return person;
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtils.closeAll(connection,preparedStatement,resultSet);
        }
        return null;
    }
    //查所有
    public List<Person> selectAll(){
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        Person person = null;
        List<Person> personList = new ArrayList<>();
        try {
            connection = DBUtils.getConnection();
            preparedStatement = connection.prepareStatement("select * from person;");
            resultSet = preparedStatement.executeQuery();
            while(resultSet.next()){
                int pid= resultSet.getInt("id");
                String name = resultSet.getString("name");
                int age = resultSet.getInt("age");
                Date bornDate = resultSet.getDate("borndate");
                String email = resultSet.getString("email");
                String address = resultSet.getString("address");
                person = new Person(pid,name,age,bornDate,email,address);
                personList.add(person);
            }
            return personList;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
  

十二. 三层架构

12.1 什么是三层
 表示层:
命名:XXXView
职责:收集用户的数据和需求、展示数据。
   
业务逻辑层:
命名:XXXServiceImpl
职责:数据加工处理、调用DAO完成业务实现、控制事务。
   
数据访问层:
命名:XXXDaoImpl
职责:向业务层提供数据,将业务层加工后的数据同步到数据库。

   
12.2 三层搭建
utils 存放工具类(DBUtils)
entity 存放实体类(Person)
dao 存放 DAO 接口(PersonDao)
        impl 存放 DAO 接口实现类(PersonDaoImpl)
service 存放 service 接口(PersonService)
        impl 存放 service 接口实现类(PersonServiceImpl)
view 存放程序启动类(main)
程序设计时,考虑易修改、易扩展,为Service层和DAO层设计接口,便于未来更换实现类。
     

十三. 通用工具类

在DAO层中,对数据库表的增、删、改、查操作存在代码冗余,可对其进行抽取封装DaoUtils工具类实现复用。
  
13.1通用DML
/**
* 将所有数据表的操作,抽离成一个公用的方法;
* @Param sql 语句
* @Param sql语句对应的参数
* @return
*/
public static boolean executeDML(String sql,Object... params){
    try {
        //获取连接
        Connection connection = getConnection();
        //获取预处理的对象
        PreparedStatement ps = connection.prepareStatement(sql);
        //设置参数
        if(null != params){
            for(int i = 0 ; i < params.length ; i++){
                //1:给占位符设置参数;2:获取参数
                ps.setObject(i+1,params[i]);
            }
        }
        int i = ps.executeUpdate();
        close(null,ps,connection);
        return i > 0;
    } catch (SQLException sqlException) {
        sqlException.printStackTrace();
    }
    return false;
}
  
13.2 通用DQL
/**
* 通用的查询;需要将我们的数据表信息,封装到List里面进行返回
* @param sql 查询语句
* @param cls 实体类的字节码对象
* @param params 查询语句里面的占位符
* @param <T> 实体类类型泛型
* @return 就是我们查询到的数据集合
*/

public static <T> List<T> executeDQL(String sql, Class<T> cls , Object...params) {
    try {
        Connection connection = getConnection(); //获取连接
        //获取预处理sql
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //设置参数
        if(null != params){
            for(int i = 0 ; i < params.length ; i++){
                preparedStatement.setObject(i+1,params[i]);
            }
        }
        //执行查询 得到结果集
        ResultSet resultSet = preparedStatement.executeQuery();
        //获取元数据(我们的列名)
        ResultSetMetaData metaData = resultSet.getMetaData();
        //列的总数
        int columnCount = metaData.getColumnCount();
        //用来保存我们所有的表数据
        List<T> list = new ArrayList<T>();
        T t;//用来保存我们一条数据
        //遍历结果集
        while(resultSet.next()){ //每一行记录的遍历过程中
            t = cls.newInstance();//根据字节码文件,构建一个实体类的对象
            //获取所有列的数据
            for(int i = 1 ; i <= columnCount ; i++){
                //你的查询语句有时候有别名
                String columnLabel = metaData.getColumnLabel(i);
                //根据列名获取我们的记录
                Object object = resultSet.getObject(columnLabel);
                //需要将拿到的这一列的记录,放到t对象指定的属性里面;
                //需要拿到这个t对象的属性,然后赋值;
                //解决方案2: 如果Object是NULL没有必要设置了
                if(null != object){
                    try {
                        Field field = cls.getDeclaredField(columnLabel);//根据属性的名称通过反射获取他的属性对象
                        field.setAccessible(true);
                        field.set(t,object);
                    } catch (NoSuchFieldException e) {
                        //代表我们当前这个实体类里面,没有这个属性
                    }
                }
            }
            //需要将t对象保存到list
            list.add(t);
        }
        close(resultSet,preparedStatement,connection);
        return list;
    } catch (SQLException sqlException) {
        sqlException.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    }
    return null;
}
   
13.3 Apache的DbUtils
13.3.1 简介
DbUtils是Java编程中数据库操作实用小工具,小巧、简单、实用。
        对于数据表的查询操作,可以把结果转换为List、Array、Set等集合。便于操作。
        对于数据表的DML操作,也变得很简单(只需要写SQL语句)。
  
13.3.2 API
ResultSetHandler接口:转换类型接口
        BeanHandler类:实现类,把一条记录转换成对象
        BeanListHandler类:实现类,把多条记录转换成List集合。
        ScalarHandler类:实现类,适合获取一行一列的数据。
QueryRunner:执行sql语句的类
        增、删、改:update();
        查询:query();
  
13.3.3 使用步骤
导入jar包
        mysql连接驱动jar包
        druid-1.1.5.jar
        database.properties配置文件
        commons-dbutils-1.6.jar
     
13.3.4 代码
package com.project.utils;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
* 连接池工具类
*/

public class DBUtils {
    private static DruidDataSource dataSource;
    static {
        Properties properties = new Properties();
        InputStream is = DBUtils.class.getResourceAsStream("/database.properties");
        try {
            properties.load(is);
            dataSource = (DruidDataSource)
            DruidDataSourceFactory.createDataSource(properties);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 返回一个数据源
    public static DataSource getDataSource(){
        return dataSource;
    }
}
package com.project.dao.impl;

import com.project.dao.UserDao;
import com.project.entity.User;
import com.project.utils.DBUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.SQLException;
import java.util.List;

public class UserDaoImpl implements UserDao {
    //1.创建QueryRunner对象,并传递一个数据源对象
    private QueryRunner queryRunner = new QueryRunner(DBUtils.getDataSource());
    @Override
    public int insert(User user) {
        Object[] params={user.getId(),user.getUsername(),user.getPassword(),user.getSex(),user.getEmail(),user.getAddress()};
        try {
            return queryRunner.update("insert into user(id,username,password,sex,email,address) values(?,?,?,?,?,?)",params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }
    @Override
    public int update(User user) {
        Object[] params={user.getUsername(),user.getPassword(),user.getSex(),user.getEmail(),user.getAdd ress(),user.getId()};
        try {
            return queryRunner.update("update user set
            username=?,password=?,sex=?,email=?,address=? where id = ?",params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }
    @Override
    public int delete(int id) {
        try {
            return queryRunner.update("delete from user where id = ?",id);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }
    @Override
    public User select(int id) {
        try {
            //把查询到的记录封装成 指定对象
            return queryRunner.query("select * from user where id = ?", new
            BeanHandler<User>(User.class), id);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
    * 查询所有
    * @return
    */
    @Override
    public List<User> selectAll() {
        try {
            return queryRunner.query("select * from user;",new
            BeanListHandler<User>(User.class));
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
}
  

十四. 连接池

在程序初始化时,预先创建指定数量的数据库连接对象存储在池中。当需要连接数据库时,从连接池中取出现有连接;使用完毕后,也不会进行关闭,而是放回池中,实现复用,节省资源。
资源重用。
更快的系统响应速度。
新的资源分配手段。
统一的连接管理,避免数据库连接泄漏。
  
14.1 Druid
14.1.1 步骤
创建 druid.properties 配置文件。
引入druid-xxx.jar 文件。
  
14.1.2 druid.properties
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mytest
username=root
password=123456
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 60000毫秒/1000等于60秒 -->
maxWait=5000
    
14.1.3 连接池工具类
public class DbUtils {
    //声明连接池对象
    private static DruidDataSource ds;
    static{
        //实例化配置对象
        Properties properties=new Properties();
        try {
            //加载配置文件内容
            properties.load(DbUtils.class.getResourceAsStream("druid.properties"));
            ds = (DruidDataSource)DruidDataSourceFactory.createDataSource(properties);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //获取连接对象
    public static Connection getConnection() {
        try {
            return ds.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
    //释放资源。。
}
   
14.2 HikariCP
引入HikariCP-xxx.jar 文件。
/*
一、主要配置
1、dataSourceClassName
这是DataSourceJDBC驱动程序提供的类的名称。请注意,如果您正在使用jdbcUrl“旧式”基于DriverManager的JDBC驱动程序配置,则不需要此属性 。 默认值:无
2、jdbcUrl
请注意,如果使用此属性,您仍然可以使用DataSource属性来配置您的驱动程序,实际上建议您使用URL本身中指定的驱动程序参数。 默认值:无
3、username
此属性设置从基础驱动程序获取连接时使用的默认身份验证用户名。
4、password
此属性设置从基础驱动程序获取连接时使用的默认身份验证密码。
*/
public static void main(String[] args) throws SQLException {
    HikariConfig hikariConfig = new HikariConfig();
    //设置url
    hikariConfig.setJdbcUrl("jdbc:mysql://localhost:3306/mytest?characterEncoding=UTF-8");
    //数据库帐号
    hikariConfig.setUsername("root");
    //数据库密码
    hikariConfig.setPassword("123456");
    HikariDataSource ds = new HikariDataSource(hikariConfig);
}
    

十五 . 批处理

addBatch(); 添加到批处理
executeBatch(); 执行批处理
clearBatch(); 清空批处理
//如果保存1w条记录
String sql = "insert into userinfo(uname,upass) values(?,?)";
Connection connection = DBUtilPool.getConnectionByDruid();
long start = System.currentTimeMillis();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for(int i = 0 ; i < 10000 ; i++){
    preparedStatement.setObject(1,"jack"+i);
    preparedStatement.setObject(2,"aaaa"+i);
    preparedStatement.addBatch(); //将当前的sql预编译好以后,加到批处理; 不会直接执行到数据库里面
    if(i % 500 == 0){
        preparedStatement.executeBatch(); //执行我们批处理
        preparedStatement.clearBatch(); //把上面的执行了,需要清空
    }
}
preparedStatement.executeBatch(); //最后一次进行批处理
long end = System.currentTimeMillis();
System.out.println("");
System.out.println(end-start);
  

十六. 事务

16.1 业务
代表用户完成的一个业务功能,可以由一个或多个DAO的调用组成。(软件所提供的一个功能都叫业务)。
  
16.2 service层

   
16.3 转账案例
BEGIN ;-- 代表我们开启了一个事务
UPDATE user_acount SET umoney = umoney-200 WHERE uname = 'jack';

-- 断网了 断电了
UPDATE user_acount SET umoney = umoney+200 WHERE uname = 'rose';

ROLLBACK -- 回滚事务(代表整个事务执行失败)
COMMIT; -- 提交事务
   
16.4 事务
事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性
原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
   
16.5 转账事务案例
-- 默认提交事务
begin;
commit; -- 提交事务
rollback; -- 事务回滚

//代码里面默认回滚事务
connection.setAutoCommit(false); //不自动提交事务
connection.commit();//提交事务
connection.rollback();//回滚事务

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/253775.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL进阶|MySQL中的事务(一)

文章目录 数据库事务MySQL中的存储引擎InnoDB存储引擎架构什么是事务事务的状态总结 数据库事务 MySQL 事务主要用于处理操作量大&#xff0c;复杂度高的数据。比方我想要删除一个用户&#xff08;销户&#xff09;以及这个用户的个人信息、订单信息以及其他信息&#xff0c;这…

12.5,12.15AVL树更新,定义,插入

定义平衡因子为右子树高度减去左子树高度 AVL树插入分为两步&#xff1a; 按照二叉搜索树的方式插入新节点调整平衡因子 对于平衡因子的调整&#xff0c;在插入之前&#xff0c;所有节点的平衡因子分为三种情况&#xff1a;0&#xff0c;1&#xff0c;-1插入后&#xff0c;新…

漏洞复现-海康威视 NCG 联网网关 login.php 目录遍历漏漏洞(附漏洞检测脚本)

免责声明 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…

C语言数据结构-----二叉树(3)二叉树相关练习题

前言 前面详细讲述了二叉树的相关知识&#xff0c;为了巩固&#xff0c;做一些相关的练习题 文章目录 前言1.某二叉树共有 399 个结点&#xff0c;其中有 199 个度为 2 的结点&#xff0c;则该二叉树中的叶子结点数为&#xff1f;2.下列数据结构中&#xff0c;不适合采用顺序存…

R语言【rgbif】——occ_search对待字符长度大于1500的WKT的特殊处理真的有必要吗?

一句话结论&#xff1a;只要有网有流量&#xff0c;直接用长WKT传递给参数【geometry】、参数【limit】配合参数【start】获取所有记录。 当我在阅读 【rgbif】 给出的用户手册时&#xff0c;注意到 【occ_search】 强调了 参数 【geometry】使用的wkt格式字符串长度。 文中如…

Springboot管理系统数据权限过滤(二)——SQL拦截器

上一节Springboot管理系统数据权限过滤——ruoyi实现方案对数据权限实现方案有了认识&#xff0c;本文将进一步优化权限过滤方案&#xff0c;实现对业务代码零入侵。 回顾上一章中权限方案&#xff1a; 主要是通过注解拦截&#xff0c;拼接好权限脚本后&#xff0c;放到对象变…

P2P如何使用register_attention_control为UNet的CrossAttention关联AttentionStore

上次的调试到这里了&#xff0c;写完这篇接着看&#xff0c;prepare_latents_ddim_inverted 如何预计算 inversion latents&#xff1a; /home/pgao/yue/FateZero/video_diffusion/pipelines/p2p_ddim_spatial_temporal.py 1. 原始的UNet3D的CrossAttention和SparseCausalAtte…

详解git pull和git fetch的区别

git pull和git fetch的区别, 网上人云亦云胡说八道的实在是太多了&#xff0c;误导我很久。 今天看到一个说得好的&#xff0c;记录一下。 前言 在我们使用git的时候用的更新代码是git fetch&#xff0c;git pull这两条指令。但是有没有小伙伴去思考过这两者的区别呢&#xff…

Docker 的基本概念、优势、及在程序开发中的应用

Docker 是一种容器化平台,它通过使用容器化技术,将应用程序及其依赖性打包到一个独立的、可移植的容器中,从而实现应用程序的快速部署、可靠性和可扩展性。 下面是 Docker 的一些基本概念和优势: 容器:Docker 使用容器化技术,将应用程序及其依赖性打包到一个可移植的容器…

[密码学]AES

advanced encryption standard&#xff0c;又名rijndael密码&#xff0c;为两位比利时数学家的名字组合。 分组为128bit&#xff0c;密钥为128/192/256bit可选&#xff0c;对应加密轮数10/12/14轮。 基本操作为四种&#xff1a; 字节代换&#xff08;subBytes transformatio…

Postman介绍和快速使用

Postman 是什么&#xff1f; Postman 是一个流行的API&#xff08;Application Programming Interface&#xff09;开发工具&#xff0c;它使得开发者可以很容易地创建、测试、共享和文档化API。Postman 提供了一个友好的用户界面&#xff0c;来发送HTTP请求&#xff0c;接收响…

bp神经网络学习

1.input(1:m,:)‘含义 矩阵A第一列的转置矩阵。(x,y)表示二维矩阵第x行第y列位置的元素&#xff0c;x为:则表示所有的行。因此&#xff0c;A(:,1)就表示A的第1列的所有元素&#xff0c;这是一个列向量。 所以这里input(1:m,:)表示1到m行&#xff0c;所有列&#xff0c;而后面…

python设计模式之工厂模式、策略模式、生产者-消费者模式

前言 这篇主要总结下 设计模式&#xff1a; 工厂模式、策略模式、生产者-消费者模式&#xff0c; 用python举例说明 一、策略模式 1.1 理论理解 顾名思义&#xff0c;根据情况来选择不一样的《策略》。 这种设计模式主要适用于&#xff1a; 希望能够根据特定条件选择方法的情况…

【谭浩强C语言:前八章编程题(多解)】

文章目录 第一章1. 求两个整数之和(p7) 第二章2. 求三个数中的较大值&#xff08;用函数&#xff09;(p14、p107)3.求123...n(求n的阶乘&#xff0c;用for循环与while循环)(P17)1.循环求n的阶乘2.递归求n的阶乘(n< 10) 4.有M个学生&#xff0c;输出成绩在80分以上的学生的学…

紫光FPGA DDR3 IP使用和注意事项(axi4协议)

紫光DDR3 IP使用 对于紫光ddr3 IP核的使用需要注意事情。 阅读ddr ip手册&#xff1a; 1、注意&#xff1a;对于写地址通道&#xff0c;axi_awvalid要一直拉高&#xff0c;axi_awready才会拉高。使用的芯片型号时PG2L100H-6FBG676&#xff0c;不同的型号IP核接口和axi的握手协…

计算机网络 网络层上 | IP数据报,IP地址,ICMP,ARP等

文章目录 1 网络层的两个层面2 网络协议IP2.1 虚拟互联网络2.2 IP地址2.2.1 固定分类编址方式2.2.2 无分类编制CIDR2.2.3 MAC地址和IP地址区别 2.3 地址解析协议ARP2.3.1 解析过程 2.4 IP数据报格式 3 IP层转发分组流程4 国际控制报文协议ICMP4.1 ICMP格式结构4.2 分类4.2.1 差…

【物联网】EMQX(二)——docker快速搭建EMQX 和 MQTTX客户端使用

一、前言 在上一篇文章中&#xff0c;小编向大家介绍了物联网必然会用到的消息服务器EMQ&#xff0c;相信大家也对EMQ有了一定的了解&#xff0c;那么接下来&#xff0c;小编从这篇文章正式开始展开对EMQ的学习教程&#xff0c;本章节来记录一下如何对EMQ进行安装。 二、使用…

系列八、约束

一、约束 1.1、概述 约束是作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据&#xff0c;通过这种规则&#xff0c;可以保证数据库中数据的正确性、有效性和完整性。 1.2、分类 1.3、注意事项 约束是作用于表中字段上的&#xff0c;可以在创建表/修改表的时候添加…

vue3的大致使用

<template><div class"login_wrap"><div class"form_wrap"> <!-- 账号输入--> <el-form ref"formRef" :model"user" class"demo-dynamic" > <!--prop要跟属性名称对应-->…

2023 OADC:开放原子云社区正式启航,Curve、Kyuubi获奖

12月16-17日&#xff0c;2023开放原子开发者大会&#xff08;OADC&#xff09;在江苏省无锡市召开。大会首日&#xff0c;由网易数帆联合发起的“开放原子云社区”宣告成立&#xff0c;随后网易数帆资深云原生专家侯诗军分享了稳定性保障的前沿实践&#xff0c;Curve、Apache K…