飞不高 发表于 2024-9-1 12:43:46

JDBC |封装JDBCUtils|PreparedStatement|事件|批处理|数据库毗连池| Blob类

一.概述

在Java中,数据库存取技能可分为如下几类:


[*]JDBC直接访问数据库
[*]JDO技能(Java Data Object)
[*]第三方O/R工具,如Hibernate, Mybatis 等
JDBC是java访问数据库的基石,JDO, Hibernate等只是更好的封装了JDBC。
1、什么是JDBC

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统(DBMS)、通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的尺度Java类库,利用这个类库可以以一种尺度的方法、方便地访问数据库资源
JDBC为访问差异的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。
JDBC的目标是使Java程序员利用JDBC可以毗连任何提供了JDBC驱动程序的数据库系统,如许就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
假如没有JDBC,那么Java程序访问数据库时是如许的:

https://i-blog.csdnimg.cn/direct/91b78429e5bb46249f11cac08ba794a8.png
https://i-blog.csdnimg.cn/direct/c9726c6efdf24ac8802553cbd41380c3.png
https://i-blog.csdnimg.cn/direct/086b34da3db347d38532d2318bc89a5a.png
2、JDBC API

   JDBC API是一系列的接口,它统一和规范了应用程序与数据库的毗连、执行SQL语句,并到得到返回效果等各类操作。声明在java.sql与javax.sql包中。
https://i-blog.csdnimg.cn/direct/5067b2cb37e84731ab228261a3a9171f.png

3、JDBC 可以做什么

我们可以做以下事情:增删改查


[*]查询女神们的基本信息
[*]查询女神们的具体信息
[*]查询心仪女神的电话号码 ,噢耶
[*]添加心仪的女神
[*]删除不符合要求的女神
[*]修改女神的信息
[*]查询管理员的所有信息
3、JDBC程序编写步调


[*]注册驱动
[*]开释资源
[*]执行增删改查
[*]获取毗连
二、演示完备步调

 预备工作:引入JDBC驱动程序

驱动程序由数据库提供商提供下载。 MySQL的驱动下载地址:http://dev.mysql.com/downloads/
怎样在Java Project项目应用中添加数据库驱动jar:

[*]https://i-blog.csdnimg.cn/direct/a2c3ad06a5994e52bd60e9df4a69aa36.png把 拷贝到项目中一个目次中
[*]https://i-blog.csdnimg.cn/direct/4f13151337ae48abb3ce6000d2dc7ef2.png(2)添加到项目标类路径下
在驱动jar上右键-->Build Path-->Add to Build Path
https://i-blog.csdnimg.cn/direct/6627476afbf44d1dac29816d930182b5.png
注意:假如是Dynamic Web Project(动态的web项目)话,则是把驱动jar放到WebContent(有的开发工具叫WebRoot)目次中的WEB-INF目次中的lib目次下即可
https://i-blog.csdnimg.cn/direct/54a3824f1a51490488a4a3c37f7762d0.png
(一)加载并注册驱动

加载并注册驱动:
加载驱动,把驱动类加载到内存
注册驱动,把驱动类的对象交给DriverManager管理,用于后面创建毗连等利用。
1、Class.forName( )

由于 Driver 接口的驱动程序类都包含了静态代码块,在这个静态代码块中,会调用 DriverManager.registerDriver() 方法来注册自身的一个实例,所以可以换一种方式来加载驱动。(即只要想办法让驱动类的这段静态代码块执行即可注册驱动类,而要让这段静态代码块执行,只要让该类被类加载器加载即可)

https://i-blog.csdnimg.cn/direct/702a7eee9ae14cde9ab28e8337cc4988.png
调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名
//通过反射,加载与注册驱动类,解耦合(不直接依靠)
Class.forName("com.mysql.jdbc.Driver");



2、服务提供者框架(例如:JDBC的驱动程序)主动注册(有版本要求)

符合JDBC 4.0规范的驱动程序包含了一个文件META-INF/services/java.sql.Driver,在这个文件中提供了JDBC驱动实现的类名。例如:mysql-connector-java-5.1.40-bin.jar文件中就可以找到java.sql.Driver文件,用文本编辑器打开文件就可以看到:com.mysql.jdbc.Driver类。

https://i-blog.csdnimg.cn/direct/b92d3703f101472798fe7e19291393d4.png


JVM的服务提供者框架在启动应用时就会注册服务,例如:MySQL的JDBC驱动就会被注册,而原代码中的Class.forName("com.mysql.jdbc.Driver")仍旧可以存在,但是不会起作用。
但是注意mysql-connector-java-5.0.8-bin.jar版本的jar中没有,如下
https://i-blog.csdnimg.cn/direct/29c105da7e274969a80fffe58acce014.png

二)获取数据库链接

可以通过 DriverManager 类创建到数据库的毗连Connection:
DriverManager 试图从已注册的 JDBC 驱动程序集中选择一个适当的驱动程序。

[*]public static Connection getConnection(String url)
[*]public static Connection getConnection(String url,String user, String password)
[*]public static Connection getConnection(String url,Properties info)此中Properties info通常至少应该包括 "user" 和 "password" 属性

JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择准确的驱动程序,从而创建到数据库的毗连。JDBC URL的尺度由三部分构成,各部分间用冒号分隔。
jdbc:<子协议>:<子名称>

[*]协议:JDBC URL中的协议总是jdbc
[*]子协议:子协议用于标识一个数据库驱动程序
[*]子名称:一种标识数据库的方法。子名称可以依差异的子协议而厘革,用子名称的目标是为了定位数据库提供充足的信息
例如:

https://i-blog.csdnimg.cn/direct/648cacad6038463c98bafb1c127b308a.png

[*]MySQL的毗连URL编写方式:   
[*]jdbc:mysql://主机名称:mysql服务端口号/数据库名称?参数=值&参数=值
[*]jdbc:mysql://localhost:3306/testdb
[*]jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=utf8(假如JDBC程序与服务器端的字符集不一致,会导致乱码,那么可以通过参数指定服务器端的字符集)
[*]jdbc:mysql://localhost:3306/testdb?user=root&password=123456

[*]Oracle9i【了解】   
[*]jdbc:oracle:thin:@主机名称:oracle服务端口号:数据库名称
[*]jdbc:oracle:thin:@localhost:1521:testdb

[*]SQLServer【了解】   
[*]jdbc:sqlserver://主机名称:sqlserver服务端口号:DatabaseName=数据库名称
[*]jdbc:sqlserver://localhost:1433:DatabaseName=testdb

        //1、加载与注册驱动
        Class.forName("com.mysql.jdbc.Driver");
       
        //2、获取数据库毗连
        String url = "jdbc:mysql://localhost:3306/test";
        Connection conn = DriverManager.getConnection(url, "root", "root");
(三)操作或访问数据库

数据库毗连被用于向数据库服务器发送下令和 SQL 语句,并担当数据库服务器返回的效果。
其实一个数据库毗连就是一个Socket毗连。
在 java.sql 包中有 3 个接口分别定义了对数据库的调用的差异方式:

[*]Statement:用于执行静态 SQL 语句并返回它所生成效果的对象。   
[*]PrepatedStatement:SQL 语句被预编译并存储在此对象中,然后可以利用此对象多次高效地执行该语句。   
[*]CallableStatement:用于执行 SQL 存储过程


Statement

通过调用 Connection 对象的 createStatement() 方法创建该对象
该对象用于执行静态的 SQL 语句,并且返回执行效果
Statement 接口中定义了下列方法用于执行 SQL 语句:
int excuteUpdate(String sql):执行更新操作INSERT、UPDATE、DELETE
ResultSet excuteQuery(String sql):执行查询操作SELECT
ResultSet

通过调用 Statement 对象的 excuteQuery() 方法创建该对象
ResultSet 对象以逻辑表格的形式封装了执行数据库操作的效果集,ResultSet 接口由数据库厂商实现
ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行
ResultSet 接口的常用方法:

[*]boolean next()
[*]getXxx(String columnLabel):columnLabel利用 SQL AS 子句指定的列标签。假如未指定 SQL AS 子句,则标签是列名称
[*]getXxx(int index) :索引从1开始
[*]…

增删改查示例代码:
public class TestConnection {
        //增删改
        @Test
        public void testUpdate() throws  Exception {
                 //步调1:注册驱动(为了让mysql的实现类加载到内存可以利用)
                
//              DriverManager.registerDriver(new Driver());
                 Class.forName("com.mysql.jdbc.Driver");
       
                 //步调2:获取毗连
                
                 Properties properties  = new Properties();//配置文件要求里面必须有毗连参数和配置参数
                
                 properties.load(new FileInputStream("src\\druid.properties"));
                 DataSource ds =        DruidDataSourceFactory.createDataSource(properties);
                
                
                 Connection connection = ds.getConnection();
                
                 //步调3:执行增删改查操作
                 //①获取执行sql语句的下令对象
                 Statement statement = connection.createStatement();
                
                
                 //②执行sql语句
//              statement.executeQuery(sql);//执行查询语句
//              statement.executeUpdate(sql)//执行增删改语法,返回受影响行数
//              statement.execute(sql)//执行任何sql语句
                
                 int update = statement.executeUpdate("delete from admin where id =1");
                
                 //③处理效果
                 System.out.println(update>0?"乐成":"失败");
                
                 //步调4:关闭毗连(开释资源)
                
                 statement.close();
                 connection.close();
                
        }
        //查询
        @Test
        public void testQuery() throws Exception {

                 //1.注册驱动
                 Class.forName("com.mysql.jdbc.Driver");              
                 //2.获取毗连
                 Properties  pro = new Properties();
                 pro.load(new FileInputStream("src\\druid.properties"));
                
                 DataSource ds = DruidDataSourceFactory.createDataSource(pro);
                
                 Connection connection = ds.getConnection();
                
                 //3.执行查询
                
                 //①获取执行sql的下令对象
                 Statement statement = connection.createStatement();
                
                 //②执行sql
                
                 ResultSet set = statement.executeQuery("select name bname,sex gender,borndate born from beauty");
                 while(set.next()) {
                        
                         String name = set.getString("bname");
                         char gender = set.getString("gender").charAt(0);
                         Date date = set.getDate("born");
        //              String date = set.getString("born");
                        
                         System.out.println(name+"\t"+gender+"\t"+date);
                
                 }
                
                 //4.关闭
                 set.close();
                 statement.close();
                 connection.close();
                
        }
       
}

https://i-blog.csdnimg.cn/direct/a110a8d9b8cc4401af6d8d334a733df8.png
四)开释资源

Connection、Statement、ResultSet都是应用程序和数据库服务器的毗连资源,利用后肯定要关闭,可以在finally中关闭
演示未关闭后果:
package com.atguigu.conn;

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

public class TestConnectionClose {
    public static void main(String[] args) throws Exception{
        //1、加载与注册驱动
        Class.forName("com.mysql.jdbc.Driver");
       
        //2、获取数据库毗连
        String url = "jdbc:mysql://localhost:3306/test";
       
        //my.ini中max_connections=10
        for (int i = 0; i < 15; i++) {
             Connection conn = DriverManager.getConnection(url,"root", "123456");
             System.out.println(conn);
             //没有关闭,资源一直没有开释
        }
    }
}
三、封装JDBCUtils


四、PreparedStatement

1、Statement的不足

(1)SQL拼接

(2)SQL注入

SQL 注入是利用某些系统没有对用户输入的数据进行充实的查抄,而在用户输入数据中注入非法的 SQL 语句段或下令,从而利用系统的 SQL 引擎完成恶意行为的做法。对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement 取代 Statement 就可以了。
(3)处理Blob类型的数据

BLOB (binary large object),二进制大对象,BLOB常常是数据库中用来存储二进制文件的字段类型。
插入BLOB类型的数据必须利用PreparedStatement,由于BLOB类型的数据无法利用字符串拼接写的。
MySQL的四种BLOB类型(除了在存储的最大信息量上差异外,他们是等同的)
https://i-blog.csdnimg.cn/direct/a4d2adf09d7447d1b3dad70d4323052f.png   假如还是报错:xxx too large,那么在mysql的安装目次下,找my.ini文件加上如下的配置参数:
  max_allowed_packet=16M

注意:修改了my.ini文件,肯定要重新启动服

现实利用中根据必要存入的数据巨细定义差异的BLOB类型
必要注意的是:假如存储的文件过大,数据库的性能会下降。

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
  `head_picture` mediumblob,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
package com.atguigu.statement;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;

import org.junit.Test;

/*
 * Statement:
 * 1、SQL拼接
 * 2、SQL注入
 * 3、处理不了Blob类型的数据
 */
public class TestStatementProblem {
   
    @Test
    public void add() throws Exception{
        Scanner input = new Scanner(System.in);
        System.out.println("请输入姓名:");
        String name = input.nextLine();
       
        System.out.println("请输入领导编号:");
        int mid = input.nextInt();
       
        System.out.println("请输入部分编号:");
        int did = input.nextInt();
       
        //1、毗连数据库
        Class.forName("com.mysql.jdbc.Driver");
       
        String url = "jdbc:mysql://localhost:3306/1221db";
        String user = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url, user, password);
       
        //2、创建Statement对象
        Statement st = conn.createStatement();
       
        //3、编写sql
        String sql = "INSERT INTO emp (ename,`mid`,did) VALUES('" + name+"'," + mid + "," + did + ")";
       
        //4、执行sql
        int update = st.executeUpdate(sql);
        System.out.println(update>0?"添加乐成":"添加失败");
       
        //5、开释资源
        st.close();
        conn.close();
    }
   
    @Test
    public void select()throws Exception{
        Scanner input = new Scanner(System.in);
        System.out.println("请输入姓名:");
        String name = input.nextLine();
       
        //1、毗连数据库
        Class.forName("com.mysql.jdbc.Driver");
       
        String url = "jdbc:mysql://localhost:3306/1221db";
        String user = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url, user, password);
       
        //2、写sql
        //孙红雷  ' or '1' = '1
        String sql = "SELECT eid,ename,tel,gender,salary FROM t_employee WHERE ename = '" + name + "'";
        System.out.println(sql);
//      SELECT eid,ename,tel,gender,salary FROM t_employee WHERE ename = '孙红雷  ' or '1' = '1'
       
        //3、用Statement执行
        Statement st = conn.createStatement();
       
        //4、执行查询sql
        ResultSet rs = st.executeQuery(sql);
        while(rs.next()){
             int id = rs.getInt(1);
             String ename = rs.getString(2);
             String tel = rs.getString(3);
             String gender =rs.getString(4);
             double salary = rs.getDouble(5);
            
             System.out.println(id+"\t" + ename + "\t" + tel + "\t" + gender + "\t" +salary);
        }
       
        //5、开释资源
        st.close();
        conn.close();
    }
   
    @Test
    public void testAddBlob(){
        String sql = "INSERT INTO `user` (username,`password`,photo)VALUES('chai','123',没法在String中处理Blob类型的数据);";
    }
}

2、PreparedStatement概述

可以通过调用 Connection 对象的 preparedStatement(String sql) 方法获取 PreparedStatement 对象
PreparedStatement 接口是 Statement 的子接口,它表现一条预编译过的 SQL 语句

[*]PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表现,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值
[*]ResultSet executeQuery()执行查询,并返回该查询生成的 ResultSet 对象。
[*]int executeUpdate():执行更新,包括增、删、该
package com.atguigu.preparedstatement;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Scanner;

import org.junit.Test;

/*
 * PreparedStatement:是Statement子接口
 * 1、SQL不必要拼接
 * 2、SQL不会出现注入
 * 3、可以处理Blob类型的数据
 * tinyblob:255字节以内
 * blob:65K以内
 * mediumblob:16M以内
 * longblob:4G以内
 *
 * 假如还是报错:xxx too large,那么在mysql的安装目次下,找my.ini文件加上如下的配置参数:
 * max_allowed_packet=16M
 * 注意:修改了my.ini文件,肯定要重新启动服务
 *
 */
public class TestPreparedStatement {
    @Test
    public void add() throws Exception {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入姓名:");
        String name = input.nextLine();
       
        System.out.println("请输入手机号码:");
        String tel = input.nextLine();
       
        System.out.println("请输入性别:");
        String gender = input.nextLine();
       
        System.out.println("请输入薪资:");
        double salary = input.nextDouble();
       
        System.out.println("请输入部分编号:");
        int did = input.nextInt();
       
        //1、毗连数据库
        Class.forName("com.mysql.jdbc.Driver");
       
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url, user, password);

        //2、编写带?的SQL
        String sql = "INSERT INTO t_employee (ename,tel,gender,salary,did) VALUES(?,?,?,?,?)";
       
        // 3、预备一个PreparedStatement:预编译sql
        PreparedStatement pst = conn.prepareStatement(sql);// 对带?的sql进行预编译

        // 4、把?用具体的值进行取代
        pst.setString(1, name);
        pst.setString(2, tel);
        pst.setString(3, gender);
        pst.setDouble(4, salary);
        pst.setInt(5, did);

        // 5、执行sql
        int len = pst.executeUpdate();
        System.out.println(len>0?"添加乐成":"添加失败");

        // 6、开释资源
        pst.close();
        conn.close();
    }

    @Test
    public void select() throws Exception {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入姓名:");
        String name = input.nextLine();
       
        //1、毗连数据库
        Class.forName("com.mysql.jdbc.Driver");
       
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url, user, password);

        //2、编写带?的sql
        //孙红雷  ' or '1' = '1
        String sql = "SELECT eid,ename,tel,gender,salary FROM t_employee WHERE ename = ?";
                
        // 3、把带?的sql语句进行预编译
        PreparedStatement pst = conn.prepareStatement(sql);

        // 4、把?用具体的变量的赋值
        pst.setString(1, name);

        // 5、执行sql
        ResultSet rs = pst.executeQuery();
        while (rs.next()) {
             int id = rs.getInt("eid");
             String ename = rs.getString("ename");
             String tel = rs.getString("tel");
             String gender = rs.getString("gender");
             double salary = rs.getDouble("salary");

             System.out.println(id + "\t" + ename + "\t" + tel + "\t" + gender + "\t" + salary);
        }

        // 6、开释资源
        rs.close();
        pst.close();
        conn.close();
    }

    @Test
    public void addBlob() throws Exception {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = input.nextLine();

        System.out.println("请输入暗码:");
        String pwd = input.nextLine();

        System.out.println("请指定照片的路径:");
        String photoPath = input.nextLine();

        //1、毗连数据库
        Class.forName("com.mysql.jdbc.Driver");
       
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url, user, password);
       
        //2、 INSERT INTO `user` VALUES(NULL,用户名,暗码,照片)
        String sql = "INSERT INTO `user` (username,`password`,head_picture)VALUES(?,?,?)";

        // 3、预备一个PreparedStatement:预编译sql
        PreparedStatement pst = conn.prepareStatement(sql);// 对带?的sql进行预编译

        // 4、对?进行设置
        pst.setString(1, username);
        pst.setString(2, pwd);
        pst.setBlob(3, new FileInputStream(photoPath));

        // 5、执行sql
        int len = pst.executeUpdate();
        System.out.println(len > 0 ? "添加乐成" : "添加失败");

        // 6、开释资源
        pst.close();
        conn.close();
    }
}

3、PreparedStatement vs Statement


[*]代码的可读性和可维护性. Statement的sql拼接是个难题。
[*]PreparedStatement 可以防止 SQL 注入
[*]PreparedStatement 可以处理Blob类型的数据
[*]PreparedStatement 能最大可能提高性能:(Oracle和PostgreSQL8是如许,但是对于MySQL不肯定比Statement高)   
[*]DBServer会对预编译语句提供性能优化。由于预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不必要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。

4、JDBC 取得数据库主动生成的主键

获取自增长的键值:
(1)在创建PreparedStatement对象时
原来:
PreparedStatement pst = conn.preparedStatement(sql);
现在:
PreparedStatement pst = conn.prepareStatement(orderInsert,Statement.RETURN_GENERATED_KEYS);
(2)原来执行更新
原来:
 int len = pst.executeUpdate();      
 现在:
int len = pst.executeUpdate();
ResultSet rs = pst.getGeneratedKeys();
if(rs.next()){
         Object key = rs.getObject(第几列);//获取自增长的键值
}      
package com.atguigu.preparedstatement;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;

public class TestGenerateKey {

    public static void main(String[] args) throws Exception{
        Scanner input = new Scanner(System.in);
        System.out.println("请输入姓名:");
        String name = input.nextLine();
       
        System.out.println("请输入手机号码:");
        String tel = input.nextLine();
       
        System.out.println("请输入性别:");
        String gender = input.nextLine();
       
        System.out.println("请输入薪资:");
        double salary = input.nextDouble();
       
        System.out.println("请输入部分编号:");
        int did = input.nextInt();
       
        //1、毗连数据库
        Class.forName("com.mysql.jdbc.Driver");
       
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url, user, password);

        //2、编写带?的SQL
        String sql = "INSERT INTO t_employee (ename,tel,gender,salary,did) VALUES(?,?,?,?,?)";
       
        // 3、预备一个PreparedStatement:预编译sql
//      执行添加语句,假如必要获取自增长的键值,那么在此处要告知mysql服务器,在创建PreparedStatement对象时,增加一个参数
        //autoGeneratedKeys - 指示是否应该返回主动生成的键的标志,它是 Statement.RETURN_GENERATED_KEYS 或 Statement.NO_GENERATED_KEYS 之一
        PreparedStatement pst = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
       
        // 4、把?用具体的值进行取代
        pst.setString(1, name);
        pst.setString(2, tel);
        pst.setString(3, gender);
        pst.setDouble(4, salary);
        pst.setInt(5, did);

        // 5、执行sql
        int len = pst.executeUpdate();
        System.out.println(len>0?"添加乐成":"添加失败");
       
        ResultSet rs = pst.getGeneratedKeys();
        if(rs.next()){
             System.out.println("新员工编号是:" + rs.getObject(1));
        }

        // 6、开释资源
        pst.close();
        conn.close();
    }

}

五、事件

JDBC程序中当一个毗连对象被创建时,默认情况下是主动提交事件:每次执行一个 SQL 语句时,假如执行乐成,就会向数据库主动提交,而不能回滚。
JDBC程序中为了让多个 SQL 语句作为一个事件执行:(重点)

[*]调用 Connection 对象的 setAutoCommit(false); 以取消主动提交事件
[*]在所有的 SQL 语句都乐成执行后,调用 commit(); 方法提交事件
[*]在此中某个操作失败或出现异常时,调用 rollback(); 方法回滚事件
[*]若此时 Connection 没有被关闭, 则必要规复其主动提交状态 setAutoCommit(true);
注意:
假如多个操作,每个操作利用的是本身单独的毗连,则无法包管事件。即同一个事件的多个操作必须在同一个毗连下
package com.atguigu.transaction;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestTransaction {
    public static void main(String[] args){
        Connection conn = null;
        try {
             //1、毗连数据库
             Class.forName("com.mysql.jdbc.Driver");

             String url = "jdbc:mysql://localhost:3306/test";
             String user = "root";
             String password = "123456";
             conn = DriverManager.getConnection(url, user, password);
             //设置手动提交
             conn.setAutoCommit(false);

             String sql1 = "update t_department set description = ? where did = ?";
             PreparedStatement pst1 = conn.prepareStatement(sql1);
             pst1.setObject(1, "挣大钱的");
             pst1.setObject(2, 4);
             int len1 = pst1.executeUpdate();
             System.out.println(len1>0?"更新部分信息乐成":"更新部分信息失败");
             pst1.close();
            
             String sql2 = "update t_employee set salary = salary + ? where did = ?";
             PreparedStatement pst2 = conn.prepareStatement(sql2);
             pst2.setObject(1, 20000);
             pst2.setObject(2, 4);
             int len2 = pst2.executeUpdate();
             System.out.println(len2>0?"更新部分信息乐成":"更新部分信息失败");
             pst2.close();
            
             conn.commit();
        }catch (Exception e) {
             try {
                 if(conn!=null){
                     conn.rollback();
                 }
             } catch (SQLException e1) {
                 e1.printStackTrace();
             }
        } finally{
             try {
                 if(conn!=null){
                     //规复主动提交
                     conn.setAutoCommit(true);
                     //开释毗连
                     conn.close();
                 }
             } catch (SQLException e) {
                 e.printStackTrace();
             }
        }
    }
}



六、批处理


当必要成批插入或者更新记载时。可以接纳Java的批量更新机制,这一机制允很多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有服从。
JDBC的批量处理语句包括下面两个方法:

[*]addBatch():添加必要批量处理的SQL语句或参数
[*]executeBatch():执行批量处理语句;
[*]clearBatch():清空批处理包的语句
通常我们会遇到两种批量执行SQL语句的情况:

[*]多条SQL语句的批量处理;
[*]
[*]https://i-blog.csdnimg.cn/direct/0ad92a826bbc430fae3a6d72646b20c2.png一个SQL语句的批量传参https://i-blog.csdnimg.cn/direct/f24d47ff700e4072b5f0bc0e49c2af7a.png注意:JDBC毗连MySQL时,假如要利用批处理功能,请再url中加参数?rewriteBatchedStatements=truePreparedStatement作批处理插入时利用values(利用value没有效果)
package com.atguigu.preparedstatement;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

import org.junit.Test;

public class TestBatch {
    @Test
    public void noBatch()throws Exception{
        Class.forName("com.mysql.jdbc.Driver");
       
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url, user, password);
       
       
        String sql = "INSERT INTO t_department(dname,description) VALUES(?,?)";
        PreparedStatement st = conn.prepareStatement(sql);
       
        for(int i=0; i<1000; i++){
           st.setString(1, "测试部门" + i);
           st.setString(2, "测试部门描述"  + i);
          
           st.executeUpdate();
        }
       
        st.close();
        conn.close();
    }
   
    @Test
    public void useBatch()throws Exception{
        Class.forName("com.mysql.jdbc.Driver");
       
        String url = "jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true";
        String user = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url, user, password);
       
       
        String sql = "INSERT INTO t_department(dname,description) VALUES(?,?)";
        PreparedStatement st = conn.prepareStatement(sql);
       
        for(int i=0; i<1000; i++){
           st.setString(1, "测试部门" + i);
           st.setString(2, "测试部门描述"  + i);
          
           st.addBatch();
        }
       
        st.executeBatch();
       
        st.close();
        conn.close();
    }
} 七、Blob类型数据的读写【选学】

MySQL中,BLOB是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳差异巨细的数据。
MySQL的四种BLOB类型(除了在存储的最大信息量上差异外,他们是等同的)
现实利用中根据必要存入的数据巨细定义差异的BLOB类型。
必要注意的是:假如存储的文件过大,数据库的性能会下降


八、数据库毗连池

1、数据库毗连池

(1)数据库毗连池的必要性

不利用数据库毗连池存在的问题:

[*]普通的JDBC数据库毗连利用 DriverManager 来获取,每次向数据库创建毗连的时候都要将 Connection 加载到内存中,再验证IP地址,用户名和暗码(得花费0.05s~1s的时间)。必要数据库毗连的时候,就向数据库要求一个,执行完成后再断开毗连。如许的方式将会消耗大量的资源和时间。数据库的毗连资源并没有得到很好的重复利用.若同时有几百人乃至几千人在线,频繁的进行数据库毗连操作将占用很多的系统资源,严峻的乃至会造成服务器的崩溃。
[*]对于每一次数据库毗连,利用完后都得断开。否则,假如程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。
[*]这种开发不能控制被创建的毗连对象数,系统资源会被毫无顾及的分配出去,如毗连过多,也可能导致内存泄漏,服务器崩溃。
为解决传统开发中的数据库毗连问题,可以接纳数据库毗连池技能(connection pool)。
数据库毗连池的基本头脑就是为数据库毗连创建一个“缓冲池”。预先在缓冲池中放入肯定数量的毗连,当必要创建数据库毗连时,只需从“缓冲池”中取出一个,利用完毕之后再放回去。数据库毗连池负责分配、管理和开释数据库毗连,它答应应用程序重复利用一个现有的数据库毗连,而不是重新创建一个。毗连池的最大数据库毗连数量限定了这个毗连池能占有的最大毗连数,当应用程序向毗连池哀求的毗连数凌驾最大毗连数量时,这些哀求将被加入到等待队列中。

https://i-blog.csdnimg.cn/direct/8b5dad4d54374ac0a983263fc9130b5a.png
数据库毗连池技能的优点:

[*]资源重用:   
[*]由于数据库毗连得以重用,避免了频繁创建,开释毗连引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的安稳性。

[*]更快的系统反应速度   
[*]数据库毗连池在初始化过程中,每每已经创建了若干数据库毗连置于毗连池中备用。此时毗连的初始化工作均已完成。对于业务哀求处理而言,直接利用现有可用毗连,避免了数据库毗连初始化和开释过程的时间开销,从而减少了系统的响应时间

[*]新的资源分配手段   
[*]对于多应用共享同一数据库的系统而言,可在应用层通过数据库毗连池的配置,实现某一应用最大可用数据库毗连数的限制,避免某一应用独占所有的数据库资源

[*]统一的毗连管理,避免数据库毗连泄漏   
[*]在较为完善的数据库毗连池实现中,可根据预先的占用超时设定,逼迫接纳被占用毗连,从而避免了通例数据库毗连操作中可能出现的资源泄漏

2)多种开源的数据库毗连池

JDBC 的数据库毗连池利用 javax.sql.DataSource 来表现,DataSource 只是一个接口,该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源构造提供实现:

[*]DBCP 是Apache提供的数据库毗连池,速度相对c3p0较快,但因自身存在BUG,Hibernate3已不再提供支持
[*]C3P0 是一个开源构造提供的一个数据库毗连池,速度相对较慢,稳固性还可以
[*]Proxool 是sourceforge下的一个开源项目数据库毗连池,有监控毗连池状态的功能,稳固性较c3p0差一点
[*]BoneCP 是一个开源构造提供的数据库毗连池,速度快
[*]Druid 是阿里提供的数据库毗连池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库毗连池,但是速度不知道是否有BoneCP快
DataSource 通常被称为数据源,它包含毗连池和毗连池管理两个部分,习惯上也常常把 DataSource 称为毗连池
注意:

[*]数据源和数据库毗连差异,数据源无需创建多个,它是产生数据库毗连的工厂,因此整个应用只必要一个数据源即可。
[*]当数据库访问竣事后,程序还是像以前一样关闭数据库毗连:conn.close(); 但conn.close()并没有关闭数据库的物理毗连,它仅仅把数据库毗连开释,归还给了数据库毗连池。
(3)Druid(德鲁伊)数据源

 Druid是阿里巴巴开源平台上一个数据库毗连池实现,它团结了C3P0、DBCP、Proxool等DB池的优点,同时加入了日志监控,可以很好的监控DB池毗连和SQL的执行情况,可以说是针对监控而生的DB毗连池,据说是目前最好的毗连池
package com.atguigu.druid;

import java.sql.Connection;
import java.util.Properties;

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSourceFactory;

public class TestDruid {
    public static void main(String[] args) throws Exception {
        Properties pro = new Properties();
        pro.load(TestDruid.class.getClassLoader().getResourceAsStream("druid.properties"));
        DataSource ds = DruidDataSourceFactory.createDataSource(pro);
        Connection conn = ds.getConnection();
        System.out.println(conn);
    }
}

url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver
initialSize=10
maxActive=20
maxWait=1000
filters=wall
具体配置参数:
配置
缺省
说明
name

配置这个属性的意义在于,假如存在多个数据源,监控的时候可以通过名字来区分开来。 假如没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this)
jdbcUrl

毗连数据库的url,差异数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username

毗连数据库的用户名
password

毗连数据库的暗码。假如你不希望暗码直接写在配置文件中,可以利用ConfigFilter。具体看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
driverClassName

根据url主动辨认 这一项可配可不配,假如不配置druid会根据url主动辨认dbType,然后选择相应的driverClassName(建议配置下)
initialSize
0
初始化时创建物理毗连的个数。初始化发生在表现调用init方法,或者第一次getConnection时
maxActive
8
最大毗连池数量
maxIdle
8
已经不再利用,配置了也没效果
minIdle

最小毗连池数量
maxWait

获取毗连时最大等待时间,单元毫秒。配置了maxWait之后,缺省启用公平锁,并发服从会有所下降,假如必要可以通过配置useUnfairLock属性为true利用非公平锁。
poolPreparedStatements
false
是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements
-1
要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements主动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery

用来检测毗连是否有效的sql,要求是一个查询语句。假如validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrow
true
申请毗连时执行validationQuery检测毗连是否有效,做了这个配置会降低性能。
testOnReturn
false
归还毗连时执行validationQuery检测毗连是否有效,做了这个配置会降低性能
testWhileIdle
false
建议配置为true,不影响性能,并且包管安全性。申请毗连的时候检测,假如空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测毗连是否有效。
timeBetweenEvictionRunsMillis

有两个寄义: 1)Destroy线程会检测毗连的间隔时间2)testWhileIdle的判断依据,具体看testWhileIdle属性的说明
numTestsPerEvictionRun

不再利用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis


connectionInitSqls

物理毗连初始化的时候执行的sql
exceptionSorter

根据dbType主动辨认 当数据库抛出一些不可规复的异常时,抛弃毗连
filters

属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters

类型是List,假如同时配置了filters和proxyFilters,是组合关系,并非替换关系

2、ThreadLocal【了解】

JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。利用这个工具类可以很轻便地编写出精美的多线程程序。
ThreadLocal用于生存某个线程共享变量,缘故原由是在Java中,每一个线程中都有一个ThreadLocalMap<ThreadLocal, Object>,其key就是一个ThreadLocal,而Object即为该线程的共享变量。而这个map是通过ThreadLocal的set和get方法操作的。对于同一个static ThreadLocal,差异线程只能从中get,set,remove本身的变量,而不会影响其他线程的变量。
1、ThreadLocal.get: 获取ThreadLocal中当火线程共享变量的值。
2、ThreadLocal.set: 设置ThreadLocal中当火线程共享变量的值。
3、ThreadLocal.remove: 移除ThreadLocal中当火线程共享变量的值。
4、ThreadLocal.initialValue: ThreadLocal没有被当火线程赋值时或当火线程刚调用remove方法后调用get方法,返回此方法值。

3、封装JDBCTools

package com.atguigu.util;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSourceFactory;

/*
 * 获取毗连或开释毗连的工具类
 */
public class JDBCTools {
    // 1、数据源,即毗连池
    private static DataSource dataSource;
   
    // 2、ThreadLocal对象
    private static ThreadLocal<Connection> threadLocal;

    static {
        try {
             //1、读取druip.properties文件
             Properties pro = new Properties();
             pro.load(JDBCTools.class.getClassLoader().getResourceAsStream("druid.properties"));
            
             //2、毗连毗连池
             dataSource = DruidDataSourceFactory.createDataSource(pro);

             //3、创建线程池
             threadLocal = new ThreadLocal<>();
        } catch (Exception e) {
             e.printStackTrace();
        }
    }

    /**
     * 获取毗连的方法
     *
     * @return
     * @throws SQLException
     */
    public static Connection getConnection() {
        // 从当火线程中获取毗连
        Connection connection = threadLocal.get();
        if (connection == null) {
             // 从毗连池中获取一个毗连
             try {
                 connection = dataSource.getConnection();
                 // 将毗连与当火线程绑定
                 threadLocal.set(connection);
             } catch (SQLException e) {
                 e.printStackTrace();
             }
        }
        return connection;
    }

    /**
     * 开释毗连的方法
     *
     * @param connection
     */
    public static void releaseConnection() {
        // 获取当火线程中的毗连
        Connection connection = threadLocal.get();
        if (connection != null) {
             try {
                 connection.close();
                 // 将已经关闭的毗连从当火线程中移除
                 threadLocal.remove();
             } catch (SQLException e) {
                 e.printStackTrace();
             }
        }
    }
}

九、Apache—DBUtils简介

commons-dbutils 是 Apache 构造提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且利用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能
DbUtils :提供如关闭毗连、装载JDBC驱动程序等通例工作的工具类,里面的所有方法都是静态的。重要方法如下:

[*]public static void close(…) throws java.sql.SQLException: DbUtils类提供了三个重载的关闭方法。这些方法查抄所提供的参数是不是NULL,假如不是的话,它们就关闭Connection、Statement和ResultSet。
[*]public static void closeQuietly(…): 这一类方法不仅能在Connection、Statement和ResultSet为NULL情况下避免关闭,还能隐藏一些在程序中抛出的SQLEeception。
[*]public static void commitAndClose(Connection conn)throws SQLException 用来提交毗连的事件,然后关闭毗连
[*]public static void commitAndCloseQuietly(Connection conn): 用来提交毗连的事件,然后关闭毗连,并且在关闭毗连时不抛出SQL异常。
[*]public static void rollback(Connection conn)throws SQLException答应conn为null,由于方法内部做了判断
[*]public static void rollbackAndClose(Connection conn)throws SQLException
[*]rollbackAndCloseQuietly(Connection)
[*]public static boolean loadDriver(java.lang.String driverClassName):这一方装载并注册JDBC驱动程序,假如乐成就返回true。利用该方法,你不必要捕获这个异常ClassNotFoundException。
2、QueryRunner类

该类封装了SQL的执行,是线程安全的。

[*]可以实现增、删、改、查、批处理、
[*]考虑了事件处理必要共用Connection。
(3)该类最重要的就是简单化了SQL查询,它与ResultSetHandler组合在一起利用可以完成大部分的数据库操作,能够大大减少编码量。
QueryRunner类提供了两个构造方法:

[*]QueryRunner():默认的构造方法
[*]QueryRunner(DataSource ds):必要一个 javax.sql.DataSource 来作参数的构造方法。
(1)更新


[*]public int update(Connection conn, String sql, Object... params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。
[*]......
(2)插入


[*]public <T> T insert(Connection conn,String sql,ResultSetHandler<T> rsh, Object... params) throws SQLException:只支持INSERT语句,此中 rsh - The handler used to create the result object from the ResultSet of auto-generated keys.  返回值: An object generated by the handler.即主动生成的键值
[*]....
(3)批处理


[*]public int[] batch(Connection conn,String sql,Object[][] params)throws SQLException: INSERT, UPDATE, or DELETE语句
[*]public <T> T insertBatch(Connection conn,String sql,ResultSetHandler<T> rsh,Object[][] params)throws SQLException:只支持INSERT语句
[*].....
(4)利用QueryRunner类实现查询


[*]public Object query(Connection conn, String sql, ResultSetHandler rsh,Object... params) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理 PreparedStatement 和 ResultSet 的创建和关闭。
[*]...... 
3、ResultSetHandler接口

该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。ResultSetHandler 接口提供了一个单独的方法:Object handle (java.sql.ResultSet  rs)该方法的返回值将作为QueryRunner类的query()方法的返回值。
该接口有如下实现类可以利用:

[*]ArrayHandler:把效果集中的第一行数据转成对象数组。
[*]ArrayListHandler:把效果集中的每一行数据都转成一个数组,再存放到List中。
[*]BeanHandler:将效果集中的第一行数据封装到一个对应的JavaBean实例中。
[*]BeanListHandler:将效果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
[*]ColumnListHandler:将效果集中某一列的数据存放到List中。
[*]KeyedHandler(name):将效果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
[*]MapHandler:将效果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
[*]MapListHandler:将效果集中的每一行数据都封装到一个Map里,然后再存放到List
4、表与JavaBean




https://i-blog.csdnimg.cn/direct/785938f6e1e8402d8037087529d188ad.png
int,double等在Java中都用包装类,由于mysql中的所有类型都可能是NULL,而Java只有引用数据类型才有NULL值
public class Employee {
        private Integer eid;
        private String ename;
        private String tel;
        private String gender;//mysql中用char,在Java中也要用String
        private Double salary;
        private Double commissionPct;
        private Date birthday;//此处用String或Date
        private Date hiredate;
        private Integer jobId;
        private String email;
        private Integer mid;
        private String address;
        private String nativePlace;
        private Integer did;
...
}
通过给列取别名的方式,来告知数据库的列名与其对应实体的属性名https://i-blog.csdnimg.cn/direct/55f58a0235fa4b4da233b1606f3308bb.png
 5.示例代码

package com.atguigu.apache.dbutils;

import java.util.List;
import java.util.Map;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.Test;

import com.atguigu.bean.Book;
import com.atguigu.util.JDBCTools;

public class TestDBUtils {
        QueryRunner qr = new QueryRunner();

        @Test
        public void testUpdate() throws Exception {
                Book book = new Book(0, "红楼梦", "曹雪芹", 88.8, 0, 200, null);
                // 写sql语句
                String sql = "insert into books(title,author,price,sales,stock,img_path) values(?,?,?,?,?,?)";
                qr.update(JDBCTools.getConnection(), sql, book.getTitle(), book.getAuthor(), book.getPrice(), book.getSales(),
                                book.getStock(), book.getImgPath());
        }

        @Test
        public void testInsert() throws Exception {
                Book book = new Book(0, "红楼梦", "曹雪芹", 88.8, 0, 200, null);
                // 写sql语句
                String sql = "insert into books(title,author,price,sales,stock,img_path) values(?,?,?,?,?,?)";
                Long id = qr.insert(JDBCTools.getConnection(), sql, new ScalarHandler<Long>(), book.getTitle(),
                                book.getAuthor(), book.getPrice(), book.getSales(), book.getStock(), book.getImgPath());
                System.out.println("新书编号:" + id);
        }

        @Test
        public void testBatch() throws Exception {
                Object[][] params = new Object;
                params = 1;
                params = 1;
                params = 1;

                params = 1;
                params = 1;
                params = 2;

                String sql = "update books set sales = sales + ? , stock = stock - ? where id = ?";
                qr.batch(JDBCTools.getConnection(), sql, params);
        }

        @Test
        public void testGetBean() throws Exception {
                // 写sql语句
                // 当JavaBean的属性名与字段名不一致时,可以通过指定别名告知属性名
                String sql = "select id,title,author,price,sales,stock,img_path imgPath from books where id = ?";
                Book b = qr.query(JDBCTools.getConnection(), sql, new BeanHandler<Book>(Book.class), 2);
                System.out.println(b);
        }

        @Test
        public void testGetBeanList() throws Exception {
                // 写sql语句
                // 当JavaBean的属性名与字段名不一致时,可以通过指定别名告知属性名
                String sql = "select id,title,author,price,sales,stock,img_path imgPath from books where price between ? and ? ";
                List<Book> list = qr.query(JDBCTools.getConnection(), sql, new BeanListHandler<Book>(Book.class), 10, 20);
                for (Book b : list) {
                        System.out.println(b);
                }
        }

        @Test
        public void testGetSingleValue() throws Exception {
                // 获取数据库中图书的总记录数
                String sql = "select count(*) from books";
                Long count = qr.query(JDBCTools.getConnection(), sql,new ScalarHandler<Long>());
                System.out.println(count);
        }
       
        @Test
        public void testGetMap() throws Exception {
                String sql = "SELECT user_id userId,COUNT(*) FROM `orders` GROUP BY user_id";
                List<Map<String, Object>> list = qr.query(JDBCTools.getConnection(), sql, new MapListHandler());
                for (Map<String, Object> map : list) {
                        System.out.println(map);
                }
        }
}
package com.atguigu.bean;

public class Book {
        private Integer id;
        private String title;
        private String author;
        private Double price;
        private Integer sales;
        private Integer stock;
        private String imgPath ="static/img/default.jpg";
        public Book(Integer id, String title, String author, Double price, Integer sales, Integer stock, String imgPath) {
                super();
                this.id = id;
                this.title = title;
                this.author = author;
                this.price = price;
                this.sales = sales;
                this.stock = stock;
                if(imgPath != null){
                        this.imgPath = imgPath;
                }
        }
        public Book() {
                super();
        }
        public Integer getId() {
                return id;
        }
        public void setId(Integer id) {
                this.id = id;
        }
        public String getTitle() {
                return title;
        }
        public void setTitle(String title) {
                this.title = title;
        }
        public String getAuthor() {
                return author;
        }
        public void setAuthor(String author) {
                this.author = author;
        }
        public Double getPrice() {
                return price;
        }
        public void setPrice(Double price) {
                this.price = price;
        }
        public Integer getSales() {
                return sales;
        }
        public void setSales(Integer sales) {
                this.sales = sales;
        }
        public Integer getStock() {
                return stock;
        }
        public void setStock(Integer stock) {
                this.stock = stock;
        }
        public String getImgPath() {
                return imgPath;
        }
        public void setImgPath(String imgPath) {
                this.imgPath = imgPath;
        }
        @Override
        public String toString() {
                return "Book [id=" + id + ", title=" + title + ", author=" + author + ", price=" + price + ", sales=" + sales
                                + ", stock=" + stock + ", imgPath=" + imgPath + "]";
        }
       
}



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: JDBC |封装JDBCUtils|PreparedStatement|事件|批处理|数据库毗连池| Blob类