【精简】JDBC编程笔记

jdbc【持续更新中】

跳转链接(学习路线)及前言(更新中)

java

mysql

jdbc

javaweb
html
css
javascript
javascriptDOM操作
vue
react





基础部分、供复习和平日查询使用,快速复习!!!
内容有误,可以评论区指出,随时改正


简介

  1. jdbc是(Java Database Connectivity)单词的缩写,翻译为java连接数据库

  1. jdbc是java程序连接数据库的技术统称

  1. jdbc由java语言的规范(接口)和各个数据库厂商的实现驱动(jar)组成

  1. jdbc是一种典型的面向接口编程

  1. jdbc优势
    1. 只需要学习jdbc规范接口的方法,即可操作所有的数据库软件

           2. 项目中期切换数据库软件,只需要更换对应的数据库驱动jar包,不需要更改代码



安装

mysql版本推荐驱动版本备注
mysql 5.5.x5.0.xcom.mysql.jdbc.Driver
mysql 5.7.x5.1.xcom.mysql.jdbc.Driver
msyql 8.x8.0.x建议: 8.0.25+省略时区设置com.mysql.cj.jdbc.Driver

下载链接(推荐8.0版本)

提取码1111





部署

添加一个lib文件夹
在这里插入图片描述



将jar包复制到该文件夹,添加为项目依赖

在这里插入图片描述



流程和讲解

核心api:
DriverManager(驱动管理)、Connection(建立连接)、Statement(发送sql语句,PreparedStatement || CallableStatement)、ResultSet(接收数据的集合类型)


  1. 注册驱动

DriverManger.registerDriver(new Driver);


注册驱动时会触发两次加载



注意:这里使用8+版本的驱动则需要选中带cj的

在这里插入图片描述


这里的registerDriver会注册一次驱动
new Driver也会再次注册一次驱动
在这里插入图片描述


所以我们需要将class文件加入到jvm虚拟机内


// 并且这种方式在更换数据库驱动时不用更改代码
Class.forName("com.mysql.cj.jdbc.Driver");


  1. 获取连接【connection建立连接】

// url:格式 jdbc:数据库厂商名://ip地址:prot/数据库名

 Connection connection = DriverManager.getConnection("url","数据库账号","数据库密码");


// 三种格式,getConnection是一个重载方法,可以使用三种方式链接数据库

// 三个参数:
// String url(数据库信息,库)语法:jdbc:数据库管理软件名称[mysql,oracle]://ip地址:port/数据库名?key=value&key=value...
// String user(数据库账号)
// String password(数据库密码)
 Connection connection = DriverManager.getConnection("url","jdbc:mysql://127.0.0.1:3306/xia","123456);



// 两个参数
// String url与三参数的url用法一样
// Properties info存储账号和密码
Properties info = new Properties();
info.put("user","root");
info.put("password","123456");
 Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/xia",info);


//一个参数
// 简写 jdbc:mysql:///xia?user=root&password=123456
DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/xia?user=root&password=123456");


// 一些数据库可选参数
serverTimeZone=ASia/Shanghai // 时区设置
useUnicode=true // 是否使用Unicode编码
characterEncoding=utf8 //设置编码格式为utf-8
useSSL // SSL验证 	


  1. 创建发送sql语句对象【statement 创建发送sql语句的statement】

// 相当于创建一个容器来装sql语句,并且送到sql内进行查询

Statement statement = connection.createStatement();


  1. 发送sql语句,并获取返回结果【statement发送sql语句到数据库 并且取得返回结构】

// 准备sql语句
String sql = "select * from 表名;

// 发送,并接收结果集合
ResultSet resultSet = statement.executeQuery(sql);

// statement有很多方法,具体使用哪个,需要看sql语句的类型

// DQL语句使用executeQuery(返回ResultSet结果封装对象)
// SQL分类:DDL(容器创建,修改,删除)DML(插入,修改,删除)DQL(查询)DCL(杈限控制)TPL(事务控制语言)

// executeUpdate(sql) 执行非DQL的语句


  1. 结果集解析【将result结果解析出来】
// resultSet内部有一个游标,指定当前行的数据,默认游标为第一行,使用next方法可以使游标向后移动一行,如果我们有多行数据,可以使用while(newx()){获取 每一行数据}

// boolean = next() 
// true: 有更多行数据,并且向下移动一行
// false: 没有更多行数据
// 光标向上移动relative方法,有很多移动光标的方法,只需要使用next配合while循环即可
// 如果next不满足需求,是查询条件有问题

while (resultSet.next()) {
	// 获取数据方法 resultSet.get类型();
	// getInt(String(clumnIndex) 传入下角标
	// getString(String columnLabel); 传入列名,如果有别名传别名
	resultSet.getInt("下角标");
	resultSet.getString("字段名");
	
	...
	
}



  1. 资源关闭【释放resultset、statement、connection】
resultSet.close();
statement.close();
connection.close();

preparedstatement基本用法


使用statement存在问题:


1.SQL语句需要字符串拼接,比较麻烦


2.只能拼接字符串类型,其他的数据库类型无法处理


3.可能发生注入攻击,statement只能使用静态的语句,否则会导致注入攻击

preparedstatement是预编译前置语句结构。

1、编写SQL语句结果,不包含动态值部分语句,动态值部分使用占位符 “ ?” 替代,注意:“ ?” 只能替代动态值


2、创建preparedstatement,并且传入动态值(先给语句结构,让其知道语句结构,不能再通过注入方式传入)


3、动态值 占位符 “ ?” 赋值


4、发送SQL语句,返回结果


//        驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

//        建立连接
        Connection connection = DriverManager.getConnection("", "", "");

// 1
        String sql = "select * from t_user where account = ? and password =?;";

// 2
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

// 3
        // 参数1:index 占位符的位置从左向右数从1开始
        // 参数2: object 占位符的值 可以设置任何类型的数据
        String account = "123";
        String password = "123";
        preparedStatement.setObject(1,account);
        preparedStatement.setObject(2,password);

// 4
        // 这里不需要再传入sql语句了,因为preparedStatement已经有该sql语句,并且动态拼接完成 
        preparedStatement.executeQuery();



preparedstatement执行DML语句


// 		注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

//      获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///数据库名","root","123456");

//      SQL语句
        String sql = "insert into table_name(字段1,字段2) values (?,?);";

//      创建preparedStatment,并传入sql
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        
//      占位符赋值
        preparedStatement.setObject(1,"测试数据");
        preparedStatement.setObject(2,1);


//      发送sql语句
        int i = preparedStatement.executeUpdate();
		
        System.out.println(i);

        preparedStatement.close();
        connection.close();




preparedstatement执行DQL语句


// 		注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

//      获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///数据库名","root","123456");

//      SQL语句
        String sql = "select * from category;";

//      创建preparedStatment,并传入sql
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

//      发送sql语句
        ResultSet resultSet = preparedStatement.executeQuery();

//      遍历数据
        while (resultSet.next()){
            System.out.println(resultSet.getInt("id"));
        }
        
//      关闭资源
        resultSet.close();
        preparedStatement.close();
        connection.close();



这种遍历数据的方式,如果字段较多时,是比较繁琐的,频繁更改。
咱们只需要把字段也遍历出内容,来动态的遍历数据就会好很多。


// 		注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

//      获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///数据库名","root","123456");

//      SQL语句
        String sql = "select * from category;";

//      创建preparedStatment,并传入sql
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

//      发送sql语句
        ResultSet resultSet = preparedStatement.executeQuery();

//      获取列信息
        ResultSetMetaData metaData = resultSet.getMetaData();

//		获取列数
        int columnCount = metaData.getColumnCount();

//      遍历表头(字段名)
        for (int i = 1; i < columnCount; i++) {
            System.out.print(metaData.getColumnLabel(i)+"\t");
        }
        System.out.println();

        while (resultSet.next()){

            for (int i = 1; i < columnCount; i++) {
                System.out.print(resultSet.getObject(i)+"\t");
            }
            System.out.println();

        }

//      关闭资源
        resultSet.close();
        preparedStatement.close();
        connection.close();



主键回显和主键值获取


返回自增长维护的主键id,就是主键回显

在创建preparedStatement时,传入第二个参数,作用:携带回数据库自增长主键


// 		注册驱动
       Class.forName("com.mysql.cj.jdbc.Driver");
       
//      获取连接
       Connection connection = DriverManager.getConnection("jdbc:mysql:///sky_take_out","root","123456");


//      SQL语句
       String sql = "insert into category(name) values(?);";

//      创建preparedStatment,并传入sql,插入参数,以要求返回主键id

        PreparedStatement preparedStatement = connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);

        preparedStatement.setObject(1,"测试的内容1");

//      发送sql语句
        int i = preparedStatement.executeUpdate();

//      获取主键id
        ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
        generatedKeys.next();
        System.out.println(generatedKeys.getInt(1));

//      遍历表头(字段名)
        //略

//      关闭资源
        generatedKeys.close();
        preparedStatement.close();
        connection.close();
        



批量插入提升性能


//        测试一万次执行效率
        Class.forName("com.mysql.cj.jdbc.Driver");


        Connection connection = DriverManager.getConnection("jdbc:mysql:///shuxin","root","123456");

        String sql = "insert into user(account,password) values(?,?);";

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        long start = System.currentTimeMillis();

        for (int i = 0; i <= 10000; i++) {
            preparedStatement.setObject(1,"ceshi"+i);
            preparedStatement.setObject(2,"ceshimima");

            preparedStatement.executeUpdate();
        }

        long end = System.currentTimeMillis();

        System.out.println("耗时:"+(end-start));   //结果为 耗时:5793

//        关闭资源
        preparedStatement.close();
        connection.close();




可以在sql语句后进行拼接
insert into user(account,password) values(?,?),values(?,?),values(?,?)…

记得在数据库路径参数设置rewriteBatchedStatements=true

记得SQL语句不要使用 “ ; ” 结尾 , 否则会报错

//      测试一万次执行效率
        Class.forName("com.mysql.cj.jdbc.Driver");


        Connection connection = DriverManager.getConnection("jdbc:mysql:///shuxin?rewriteBatchedStatements=true","root","123456");

        String sql = "insert into user(id,account,password) values(?,?,?)";

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        long start = System.currentTimeMillis();

        for (int i = 0; i <= 10000; i++) {
        
            preparedStatement.setObject(1,i);
            preparedStatement.setObject(2,"ceshi"+i);
            preparedStatement.setObject(3,"ceshimima");

//			不执行,将后续的值添加到sql语句后方
            preparedStatement.addBatch();
        }

//		执行批量操作
        preparedStatement.executeBatch();
        
        long end = System.currentTimeMillis();

        System.out.println("耗时:"+(end-start)); //结果为 耗时:171

//      关闭资源
        preparedStatement.close();
        connection.close();





事务实现

关闭/开启自动提交


// connection是用来操作事务的,statement是对应单一的数据库动作
Connection.setAutoCommit(false/true);

// 数据库操作自动事务提交 开启/关闭
set autocommit = 1/0;


在jdbc中,我们使用try/catch来操作事务是否提交和回滚。当程序报错时,我们就回滚,反之提交



格式


	try {
            connection.setAutoCommit(false);

            //...
            //connection操作事务
            //statement代表数据库的单一动作

            connection.commit();
        } catch (Exception e){
			connection.rollback();
        }
        



案例

转账案例
1、设计Service类,定义一个金额修改方法,可以根据转账用户名和被转账用户名修改金额

public class BankService {

    @Test
    public void testTransfer() throws Exception {
        transfer("1","2",2000);
    }
    public void transfer(String addAccount,String subAccount,int money) throws Exception {
        BankDao bankDao = new BankDao();
//        转钱
        bankDao.addMoney(addAccount,money);
//        减钱
        bankDao.subMoney(subAccount,money);
    }
}



2、加钱减钱不在Service内操作,而是设计一个Dao类,里面定义两个方法,一个加钱,一个扣钱

public class BankDao {

    public void addMoney(String account, int money) throws Exception {

//        注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

//        连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///shuxin", "root", "123456");

//      sql语句,创建preparedStatment,并传入sql
        String sql = "update bank set money = money + ? where id = ?;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

//      占位符赋值
        preparedStatement.setObject(1, money);
        preparedStatement.setObject(2, account);

//      发送sql语句
        int i = preparedStatement.executeUpdate();

//      反馈结果
        if (i > 0) {
            System.out.println("加钱成功!");
        } else if (i == 0) {
            System.out.println("用户名不存在!");
        } else {
            System.out.println("加钱失败!");
        }
    }
    
    public void subMoney(String account, int money) throws Exception {
        // ... 
    }
}



这种方式呢,没有事务,当某个账户没有钱了(在表设计字段时,设计为无符号),那么会报错,某个账户则会多出来钱



注意:

一个事务必须在同一个连接(同一个Connection对象),所以我们不能用这种方式来操作事务,需要将两个操作合并到一起。

这段内容注释要重点看喔!


//  修改Service类( 修改内容说明在括号里!!! )
    public void transfer(String addAccount,String subAccount,int money) throws Exception {
        
        BankDao bankDao = new BankDao();
        
//      注册驱动( 这里创建一个Connection,给两个方法传入共用 )
        Class.forName("com.mysql.cj.jdbc.Driver");

//      连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///shuxin", "root", "123456");

        try{
        
//     		关闭事务自动提交( 不要让数据库自动提交 )
            connection.setAutoCommit(false);
            
//	        执行数据库操作( 记得传入connection )
            bankDao.addMoney(addAccount,money,connection);
            bankDao.subMoney(subAccount,money,connection);

//      	提交事务( 执行加钱减钱操作后,执行事务提交 )
            connection.commit();

        }
        catch (Exception e){
        
//      	事务回滚( 执行时,当钱是负数时,虽然加钱是成功的,但减钱操作时失败的,所以会触发catch内的代码,回滚到操作前的数据 )
            connection.rollback();
            
//      	异常信息建议抛出,因为该方法也是被Test类调用的
            throw e;
            
        }
        

//  修改Dao类( 修改内容说明在括号里!!! )


//	( 添加一个方法形参,共用一个Connection )
    public void addMoney(String account, int money,Connection connection) throws Exception {

//		( 删除Connection连接,在外部调用时传入一个共用的Connection )

        String sql = "update bank set money = money + ? where id = ?;";

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        preparedStatement.setObject(1, money);
        preparedStatement.setObject(2, account);


        int i = preparedStatement.executeUpdate();
        if (i > 0) {
            System.out.println("加钱成功!");
        } else if (i == 0) {
            System.out.println("用户名不存在!");
        } else {
            System.out.println("加钱失败!");
        }

//      关闭资源( 不需要将connection关闭,因为这两个方法的操作合并到一个事务,在没有执行完时是不要关闭的 )
        preparedStatement.close();
    }

    public void subMoney(String account, int money,Connection connection) throws Exception {

        // ...
    }


连接池

简介

druid是阿里开发的一款数据库连接池,他能有效的提升开发速度和运行效率。


我们发现在jdbc中经常使用Connection这个对象,还有关闭资源的对应方法,会大大的浪费效率。


甚至可能连接和关闭资源的占用都比使用的时间长。


druid能帮助我们管理连接和关闭资源

连接池有最大连接数量,到达最大数量时,需要等待一下


市面上有很多连接池产品,Javax.sql.DataSource接口规范了连接池获取连接的方法和回收连接的方法。



使用

准备工作——>


引入jar包,去中央仓库下载


https://mvnrepository.com/search?q=druid

在这里插入图片描述


选择一个使用人数较多的
在这里插入图片描述



点击这里
在这里插入图片描述



选择jar
在这里插入图片描述



1、创建druid连接池对选哪个


DruidDataSource dataSource = new DruidDataSource();



2、设置连接池参数


// 必须设置
dataSource.setUrl("jdbc:mysql://localhost:3306/shuxin");
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");

// 可选的
//		初始化连接数量
        dataSource.setInitialSize(5);
//      最大连接数量
        dataSource.setMaxActive(10);



3、获取连接(通用方法,所有的连接池都一样)


//        获取连接
        DruidPooledConnection connection = dataSource.getConnection();



4、回收连接(通用方法,所有的连接池都一样)

//      回收资源
        connection.close();
//  	这里经过连接池,为回收资源



使用配置文件的方式

1、在src路径下创建一个druid.propreties的文件,并写入配置文件


driverClassName=com.mysql.cj.jdbc.Driver
username=root
password = root
url=jdbc:mysql:///shuxin



2、读取外部配置文件


InputStream ips = JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");

Properties properties = new Properties();

properties.load(ips);



3、使用连接池的工具类的工程模式,创建连接池


//        使用连接池工具类的工程模式,创建连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);

        Connection connection = dataSource.getConnection();

<br><br>

4、数据库操作




5、回收连接


 connection.close();



工具类封装

1、在src下,创建一个文件夹用于存放我们自己封装的工具类

在这里插入图片描述


2、创建一个工具类


    private static DataSource dataSource = null;


    static {
    
//      初始化连接池对象
//      加载外部配置文件
        InputStream ips = JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");

        Properties properties = new Properties;

        try {
            properties.load(resourceAsStream);
            
        } catch (IOException e) { throw new RuntimeException(e); }

        try {
        
            dataSource = DruidDataSourceFactory.createDataSource(properties);
            
        } catch (Exception e) { throw new RuntimeException(e); }
    }

    //    对外返回连接池方法
    public static Connection getConnection() throws SQLException {
    
        return dataSource.getConnection();
    }

    //    回收连接方法
    public static void freeConnection(Connection connection) throws SQLException {
    
        connection.close();
        
    }
    



完善工具类

首先,我们在使用我们上面写的工具类,只要调用一次getConnection方法,就会创建一个连接。同时我们是在同一个线程中,也就是说,我们在同一个线程中创建了多个数据库连接。我们应该实现的是,在同一个线程中使用同一个连接(不同方法)。


jdk1.2的版本中就提供java.lang.ThreadLocal,为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。通常用来在在多线程中管理共享数据库连接、session等


我们如果不使用这种共享连接的方法,那么在Dao层的方法中必然需要传递一个connection形参,并且在Service层需要创建连接传入,这样不仅代码稍显复杂,也会降低性能。



public class JdbcUtils {

    // 数据库来源(存放连接池)
    private static DataSource dataSource = null;

    // 线程本地变量
    private static ThreadLocal<Connection> tl = new ThreadLocal<>();

//        初始化druid
    static {

//        读取外部配置文件
        InputStream ips = JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");


//        用于加载
        Properties properties = new Properties();

        try {
            properties.load(resourceAsStream);
        } catch (IOException e) { throw new RuntimeException(e); }

//        使用druid工具类的工厂模式创建连接池
        try {
        
            dataSource = DruidDataSourceFactory.createDataSource(properties);
            
        } catch (Exception e) { throw new RuntimeException(e); }  }
    
    //        获取连接
    public static Connection getConnection() throws SQLException {

        Connection connection = tl.get();

        //        第一次连接,线程本地变量是没有连接的
        if (connection == null){
//            没有的话获取一个connction
            connection = dataSource.getConnection();
            tl.set(connection);
        }
            return connection;
    }

//    回收连接
    public static void freeConnection() throws SQLException {

        Connection connection = tl.get();
        if(connection != null){
//        如果线程本地变量是有内容的(连接),那么我们需要将其移除
            tl.remove();
//            并且事务设置为true,因为如果设置了手动提交的话,其值为false,所以我们需要设置为true,让其回归默认状态
            connection.setAutoCommit(true);
        } } } 



使用完善后的工具类

bankDao.java


public class BankDao {

    public void addMoney(String account, int money,Connection connection) throws Exception {

        String sql = "update bank set money = money + ? where id = ?;";

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        preparedStatement.setObject(1, money);
        preparedStatement.setObject(2, account);


        int i = preparedStatement.executeUpdate();
        if (i > 0) {
            System.out.println("加钱成功!");
        } else if (i == 0) {
            System.out.println("用户名不存在!");
        } else {
            System.out.println("加钱失败!");
        }

//        关闭资源
        preparedStatement.close();
    }

    public void subMoney(String account, int money,Connection connection) throws Exception {

      // ... } }



bankService.java

public class BankService {

    @Test
    public void testTransfer() throws Exception {
        transfer("2","1",2000);
    }
    public void transfer(String addAccount,String subAccount,int money) throws Exception {
    
        BankDao bankDao = new BankDao();

        Connection connection = JdbcUtils.getConnection();

        try{
        
//      开启事务
            connection.setAutoCommit(false);
            
//      执行数据库操作
            bankDao.addMoney(addAccount,money);
            bankDao.subMoney(subAccount,money);

//      提交事务
            connection.commit();
            
        }
        catch (Exception e){
        
//      事务回滚
            connection.rollback();

//      异常信息建议抛出,因为该方法也是被Test类调用的
            throw e;
            
        }finally {

//          关闭Connection连接
            JdbcUtils.freeConnection();
        } } }

非DQL方法封装

jdbc使用步骤
1、注册驱动
2、获取连接
3、编写SQL语句
4、创建satatment(PreparedStatement)
5、占位符赋值
6、发送SQL语句
7、结果解析
8、回收资源



我们的工具类简化了1、2、8的步骤,剩下的我们还需要继续简化,我们每个表都有一个Dao类,当我们在操作表时,重复的内容可以再创建一个类来共用,命名为BaseDao。将重复的代码抽取到BaseDao中。

    public int excuteUpdate(String sql, Object... prams) throws SQLException {

//        获取连接
        Connection connection = JdbcUtils.getConnection();

//      创建一个小车用来装sql语句
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        connection.setAutoCommit(false);
        try{
            //       占位符遍历
            for (int i = 0; i < prams.length; i++) {
                preparedStatement.setObject(i+1, prams[i]);

            }
        }
        catch (Exception e){
            connection.rollback();
            e.printStackTrace();
        }

    connection.commit();
//         发送sql语句
        int rows = preparedStatement.executeUpdate();

//         回收
        preparedStatement.close();

//        如果autocommit(自动提交为true,代表是自动提交,那么就没有开启事务,可以关闭连接)
        if (connection.getAutoCommit()) {
            JdbcUtils.freeConnection();
        }

        return rows;
    }



DQL方法封装


protected <T> ArrayList<T> query(Class<T> clazz,String sql, Object... args) throws Exception {
        //        创建PreparedStatement对象,对sql预编译
        Connection connection = JDBCTools.getConnection();
        PreparedStatement ps = connection.prepareStatement(sql);
        //设置?的值
        if(args != null && args.length>0){
            for(int i=0; i<args.length; i++) {
                ps.setObject(i+1, args[i]);//?的编号从1开始,不是从0开始,数组的下标是从0开始
            }
        }

        ArrayList<T> list = new ArrayList<>();
        ResultSet res = ps.executeQuery();

        /*
        获取结果集的元数据对象。
        元数据对象中有该结果集一共有几列、列名称是什么等信息
         */
         ResultSetMetaData metaData = res.getMetaData();
        int columnCount = metaData.getColumnCount();//获取结果集列数

        //遍历结果集ResultSet,把查询结果中的一条一条记录,变成一个一个T 对象,放到list中。
        while(res.next()){
            //循环一次代表有一行,代表有一个T对象
            T t = clazz.newInstance();//要求这个类型必须有公共的无参构造

            //把这条记录的每一个单元格的值取出来,设置到t对象对应的属性中。
            for(int i=1; i<=columnCount; i++){
                //for循环一次,代表取某一行的1个单元格的值
                Object value = res.getObject(i);

                //这个值应该是t对象的某个属性值
                //获取该属性对应的Field对象
                //String columnName = metaData.getColumnName(i);//获取第i列的字段名
                //这里再取别名可能没办法对应上
                String columnName = metaData.getColumnLabel(i);//获取第i列的字段名或字段的别名
                Field field = clazz.getDeclaredField(columnName);
                field.setAccessible(true);//这么做可以操作private的属性

                field.set(t, value);
            }

            list.add(t);
        }

        res.close();
        ps.close();
        //这里检查下是否开启事务,开启不关闭连接,业务方法关闭!
        //没有开启事务的话,直接回收关闭即可!
        if (connection.getAutoCommit()) {
            //回收
            JDBCTools.free();
        }
        return list;
    }


  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值