Java——数据库编程JDBC之快速入门吐血总结及各关键对象详解(提供了JDBCUtils工具类)

    技术2022-07-12  64

    目录

    1 JDBC基本概念

    2 JDBC的快速入门

    3 JDBC各对象详解

    3.1 DriverManager驱动管理对象

    3.2 Connection数据库连接对象

    3.3 Statement执行SQL的对象

    3.4 ResultSet结果集对象

    3.4.1 ResultSet的基本使用

    3.4.2 ResultSet进阶练习(查询的数据封装为对象)

    3.4.3 抽取JdbcUtils工具类

    3.5 PreparedStatement执行SQL的对象

    4 JDBC事务管理


    1 JDBC基本概念

    JDBC,Java Database Connectivity,Java数据库连接,Java语言操作数据库。JDBC的本质是SUN公司定义的一套操作所有关系型数据库的规则,即接口,各数据库厂商实现这套接口,提供数据库驱动jar包,用户可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。

    2 JDBC的快速入门

    使用JDBC的步骤:

    1)导入驱动jar包:mysql-connector-java-5.1.37-bin.jar;          -- 复制jar包到项目的libs目录下;           -- 右键,Add As Library;2)编写代码,注册驱动;3)获取数据库的连接对象;4)定义sql语句;5)获取执行sql语句的对象statement;6)执行sql,接收返回结果;7)处理结果;8)释放资源,否则可能会造成内存的泄露问题。

    快速入门代码如下:

    public class JdbcDemo1 { public static void main(String[] args) throws Exception { //1)导入驱动jar包 //2)注册驱动 Class.forName("com.mysql.jdbc.Driver"); //3)获取数据库连接对象 Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1","root","root"); //4)定义sql语句 String sql = "update account set balance = 500 where id = 2"; //5)获取执行sql的对象Statement Statement statement = connection.createStatement(); //6)执行sql int count = statement.executeUpdate(sql); //7)处理结果 System.out.println(count); //8)释放资源 statement.close(); connection.close(); } }

    3 JDBC各对象详解

    3.1 DriverManager驱动管理对象

    1)注册驱动:告诉程序该使用哪一个数据库驱动jar

    static void registerDriver(Driver driver),注册与给定的驱动程序DriverManager,但是写代码时使用的是Class.forName("com.mysql.jdbc.Driver"),为什么?查看源码发现,在com.mysql.jdbc.Driver类中存在静态代码块:

    【注意】:实际上mysql5以后的驱动jar包可以省略注册驱动的步骤,若没有写,程序会读取下面的文件实现自动注册,但是,实际开发中还是写上,避免有误区;

    2)获取数据库连接

    方法:public static java.sql.Connection getConnection(String url,  String user, String password);

    参数:url:指定连接的路径,写法:jdbc:mysql://ip地址(域名):端口号/数据库名称;            user:用户名            password:密码

    【注意】:若连接是本机的mysql服务器,且mysql默认端口是3306,则url可以简写为jdbc:mysql:///数据库名称;

    3.2 Connection数据库连接对象

    1)获取执行sql的对象

    Statement createStatement()PreparedStatement prepareStatement(String sql)

    2)管理事务

    开启事务:setAutoCommit(boolean autoCommit):设置参数为false,即开启事务;提交事务:commit();回滚事务:rollback();

    3.3 Statement执行SQL的对象

    1)执行sql

    boolean execute(String sql):执行给定的sql语句,返回可能的结果(了解即可,不常用)。int executeUpdate(String sql):执行DML(insert、update、delete)语句、DDL(create、alter、drop)语句。 返回值:影响的行数,可以通过这个结果判断DML语句是否执行成功,返回值>0则成功,反之则失败。

            

    ResultSet executeQuery(String sql):执行DQL(select)语句。

    【举例】:在account表中添加一条记录、修改记录、删除一条记录,要考虑异常处理及资源释放的规范性。

    public class JdbcDemo2 { public static void main(String[] args) { Connection connection = null; Statement statement = null; try { //1、注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2、定义sql String sql = "insert into account values(null,'wangsan',2000)"; //3、获取Connection对象 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "root"); //4、获取执行sql对象 statement = connection.createStatement(); //5、执行sql int cnt = statement.executeUpdate(sql); //6、处理结果 System.out.println(cnt); if(cnt>0){ System.out.println("添加成功"); }else{ System.out.println("添加失败"); } } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); }finally { //7、释放资源 //要避免空指针异常 if(statement!=null){ try { statement.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if(connection!=null){ try { connection.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } } }

    以上程序为添加一条记录,修改、删除记录与其相似,只需要改下sql语句:

    修改:String sql = "update account set balance = 1500 where id =3";删除:String sql = "delete from account where id =3";

    3.4 ResultSet结果集对象

    Statement的executeQuery方法,返回的就是ResultSet结果集对象,用户需要做的就是把结果从ResultSet中取出来,涉及到两类方法:

    next()方法:游标向下移动一行(游标默认是在表头处),判断当前行是否是最后一行之后(是否有数据),若是,则返回false,否则返回true;getXxx(参数)方法:获取一列的数据;      Xxx代表数据类型,如String getString();      参数:Int,代表列的编号,从1 开始,如getString(1);                 String,代表列的名称,如getDouble("balance");

    3.4.1 ResultSet的基本使用

    public class JdbcDemo3 { public static void main(String[] args) { Connection connection = null; Statement statement = null; ResultSet resultSet = null; try { //1、注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2、定义sql String sql = "select * from account"; //3、获取Connection对象 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "root"); //4、获取执行sql对象 statement = connection.createStatement(); //5、执行sql resultSet = statement.executeQuery(sql); //6、处理结果 //6.1 先让游标向下移动一行 resultSet.next(); int id = resultSet.getInt(1); String name = resultSet.getString("name"); double balance = resultSet.getDouble(3); System.out.println(id+ "--"+ "name"+"--"+balance); } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); }finally { //7、释放资源 //要避免空指针异常 if(resultSet!=null){ try { resultSet.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if(statement!=null){ try { statement.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if(connection!=null){ try { connection.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } } }

    以上只是ResultSet的基本使用,前提是我们知道表中的有几条数据,但实际查询中我们不能确定查询到的结果是多少条,有存在游标超限等异常可能。

    【注意使用步骤】:

    1)游标向下移动一行;2)判断是否有数据(仍使用next()方法);3)获取数据;

    所以,将以上代码中,处理结果的语句改进如下:

    //6、处理结果 //6.1 先让游标向下移动一行 while(resultSet.next()){ int id = resultSet.getInt(1); String name = resultSet.getString("name"); double balance = resultSet.getDouble(3); System.out.println(id+ "--"+ "name"+"--"+balance); }

    3.4.2 ResultSet进阶练习(查询的数据封装为对象)

    【要求】:查询emp表中的数据,将其封装为对象,然后装载集合返回。

    【实现思路】:

    1)定义Emp类;2)定义方法public List<Emp> findAll(){}3)实现方法:select * from emp;

    【源码】:其中,可以以findAll方法为模板,封装个自己的mysql的查询工具类

    1)JdbcDemo.java

    public class JdbcDemo4 { public static void main(String[] args) { //测试 List<Emp> list = new JdbcDemo4().findAll(); System.out.println(list.size()); System.out.println(list); } public List<Emp> findAll(){ Connection connection = null; Statement statement = null; ResultSet resultSet = null; List<Emp> list = null; try { //1、注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2、定义sql String sql = "select * from emp"; //3、获取Connection对象 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "root"); //4、获取执行sql对象 statement = connection.createStatement(); //5、执行sql resultSet = statement.executeQuery(sql); //6、处理结果 //遍历结果集,封装对象,装载集合 Emp emp = null; list = new ArrayList<>(); while(resultSet.next()){ int id = resultSet.getInt("id"); String name = resultSet.getString("name"); String gender = resultSet.getString("gender"); double salary = resultSet.getDouble("salary"); Date date = resultSet.getDate("join_date"); int dept_id = resultSet.getInt("dept_id"); emp = new Emp(); emp.setId(id); emp.setName(name); emp.setGender(gender); emp.setSalary(salary); emp.setJoin_date(date); emp.setDept_id(dept_id); list.add(emp); } } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); }finally { //7、释放资源 //要避免空指针异常 if(resultSet!=null){ try { resultSet.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if(statement!=null){ try { statement.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if(connection!=null){ try { connection.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } return list; } }

    2)Emp.java 类

    public class Emp { private int id; private String name; private String gender; private Double salary; private Date join_date; private int dept_id; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } public Date getJoin_date() { return join_date; } public void setJoin_date(Date join_date) { this.join_date = join_date; } public int getDept_id() { return dept_id; } public void setDept_id(int dept_id) { this.dept_id = dept_id; } @Override public String toString() { return "Emp{" + "id=" + id + ", name='" + name + '\'' + ", gender='" + gender + '\'' + ", salary=" + salary + ", joindate=" + join_date + ", dept_id=" + dept_id + '}'; } }

    3.4.3 抽取JdbcUtils工具类

    由上一节我们可以发现,完成JDBC操作时,代码重复度特别高,每一次操作都要获取连接----释放资源,所以可以写一个工具类,来简化代码书写:

    抽取一个方法注册驱动;抽取一个方法获取连接对象;   需求:不传递参数,比较麻烦,还得保证工具类的通用性;    解决:通过配置文件解决此问题,提供一个文件jdbc.properties,定义url=...,user=...,password=...,读取配置文件即可获取参数,参数发生变化时只需该配置文件,无需改动代码。抽取一个方法释放资源

    【JdbcUtils工具类源码】

    //JDBC工具类 public class JdbcUtils { private static String url; private static String user; private static String password; private static String driver; /** * 文件的读取只需要读取一次 * */ static { //读取资源文件,获取值 try { //1、创建Properties Properties pro = new Properties(); //2、加载文件 //动态获取src路径下的文件的方式 类加载器 ClassLoader loader = JdbcUtils.class.getClassLoader(); URL resource = loader.getResource("jdbc.properties"); String path = resource.getPath(); pro.load(new FileReader(path)); //3、获取属性赋值 url = pro.getProperty("url"); user = pro.getProperty("user"); password = pro.getProperty("password"); driver = pro.getProperty("driver"); //4、注册驱动 Class.forName(driver); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } /* * 获取连接 * * */ public static Connection getConnection() throws SQLException{ return DriverManager.getConnection(url,user,password); } /* * 释放资源 * * */ public static void close(Statement stmt,Connection conn){ if(stmt!=null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } /* * 释放资源 * * */ public static void close(ResultSet rs, Statement stmt, Connection conn){ if(rs!=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stmt!=null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

    【举例】:通过键盘录入用户名及密码,判断用户登录成功或失败

    1)先准备user数据库表

    CREATE TABLE USER( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20), psd VARCHAR(20) ); INSERT INTO USER VALUES(NULL,'zhangsan','123'); INSERT INTO USER VALUES(NULL,'lisi','456'); SELECT * FROM USER;

    2)登录方法 代码及测试

    public class JdbcDemo6 { public static void main(String[] args) { //键盘输入 Scanner scanner = new Scanner(System.in); System.out.println("请输入用户名:"); String usr = scanner.nextLine(); System.out.println("请输入密码:"); String psd = scanner.nextLine(); //登录验证 Boolean b = login(usr,psd); if(b){ System.out.println("登录OK"); }else{ System.out.println("登录fail"); } } public static boolean login(String user,String psd){ Connection connection = null; Statement statement = null; ResultSet resultSet = null; if(user == null || psd == null){ return false; } try { connection = JdbcUtils.getConnection(); String sql = "select * from user where name = '"+user+"' and psd = '"+psd+"' "; statement = connection.createStatement(); resultSet = statement.executeQuery(sql); return resultSet.next(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { JdbcUtils.close(resultSet,statement,connection); } return false; } }

    3.5 PreparedStatement执行SQL的对象

    1)SQL注入问题:在拼接sql时,有一些特殊的关键字参与字符的拼接,会造成安全性问题。

    以上登录验证,实际上没有这么干的,因为质量太差,我们试验下,输入如下用户名及密码,看下结果如何:

    随便输入的用户名和密码,竟然也登录成功了。以上的问题就是SQL注入问题,看打印出的sql语句,or 'a' = 'a',这是个恒等式,所以才会出现上述的问题。

    2)解决SQL注入问题:使用PreparedStatement对象

    预编译SQL:参数使用?作为占位符

    使用步骤:

    1)导入驱动jar包:mysql-connector-java-5.1.37-bin.jar;2)编写代码,注册驱动;3)获取数据库的连接对象;4)定义sql语句;       注意:sql的参数使用?作为占位符,如select * from user where user= ? and psd = ?;5)获取执行sql语句的对象PreparedStatement,Connection.prepareStatement(String sql);6)给?赋值,使用PreparedStatement中的方法,setXxx(参数1,参数2);参数1 :?位置,从1 开始,参数2:?的值;7)执行sql(不需要传递sql语句),接收返回结果;8)处理结果;9)释放资源,否则可能会造成内存的泄露问题。

    【注意】:后期都会使用PrepareStatement对象完成增删改查的所有操作,可以防止SQL注入,且效率更高,不会再用Statement对象。

    使用PrepareStatement完成上述的 登录验证案例如下:

    public static boolean login2(String user,String psd){ Connection connection = null; PreparedStatement statement = null; ResultSet resultSet = null; if(user == null || psd == null){ return false; } try { connection = JdbcUtils.getConnection(); String sql = "select * from user where name = ? and psd = ? "; // System.out.println(sql); statement = connection.prepareStatement(sql); //给?赋值 statement.setString(1,user); statement.setString(2,psd); resultSet = statement.executeQuery(); return resultSet.next(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { JdbcUtils.close(resultSet,statement,connection); } return false; }

    4 JDBC事务管理

    关于事务可以看下这篇博客,使用JDBC控制事务,需要使用Connection对象来管理事务:

    开启事务:setAutoCommit(boolean autoCommit),调用方法时参数为false,在执行sql之前开启事务;提交事务:commit(),当所有sql都执行完提交事务;回滚事务:rollback(),在catch中回滚事务。

    【举例】:使用JDBC进行事务管理的示例,zhangsan -500,lisi +500,注意何时开启事务,何时提交事务,何时回滚事务。

    public class JdbcDemo7 { public static void main(String[] args) { Connection connection = null; PreparedStatement pstmt1 = null; PreparedStatement pstmt2 = null; try { //1、获取连接 connection = JdbcUtils.getConnection(); connection.setAutoCommit(false);//开启事务 //2、定义sql String sql1 = "update account set balance = balance - ? where id = ?"; String sql2 = "update account set balance = balance + ? where id = ?"; //3、获取执行sql对象 pstmt1 = connection.prepareStatement(sql1); pstmt2 = connection.prepareStatement(sql2); //4、设置参数 pstmt1.setDouble(1,500); pstmt1.setInt(2,1); pstmt2.setDouble(1,500); pstmt2.setInt(2,2); //5、执行sql pstmt1.executeUpdate(); //手动制造异常 int i = 3/0; pstmt2.executeUpdate(); connection.commit(); //提交事务 } catch (Exception e) { try { if(connection != null){ connection.rollback();//事务回滚 } } catch (SQLException throwables) { throwables.printStackTrace(); } e.printStackTrace(); }finally { JdbcUtils.close(pstmt1,connection); JdbcUtils.close(pstmt2,null); } } }

     

    作于202007051615,已归档

    ———————————————————————————————————

    本文为博主原创文章,转载请注明出处!

    若本文对您有帮助,轻抬您发财的小手,关注/评论/点赞/收藏,就是对我最大的支持!

    祝君升职加薪,鹏程万里!

    Winter_world 认证博客专家 嵌入式开发 Android JavaWeb 一个只喜欢带干货,不为吸引眼球而弄些花哨软文的博主;一个秉承活到老学到老精神的双985高校毕业研究僧;一个曾就职于华为公司,敢于拼搏、项目经验丰富的工程师;一个从硬件、嵌入式、互联网多路径全面开花的全栈达人;点击下方关注,博主将增加无限动力分享更多干货,愿与您相伴,不负韶华,奔向更好的明天!
    Processed: 0.020, SQL: 9