后端开发——JDBC的学习(三)
本篇继续对JDBC进行总结:
①通过Service层与Dao层实现转账的练习;
②重点:由于每次使用连接就手动创建连接,用完后就销毁,这样会导致资源浪费,因此引入连接池,练习连接池的使用;
③实现一个工具类,不用每次都手写获取连接以及配置数据库要素等,并且对工具类进行优化;然后使用连接池以及工具类对前部分转账部分的练习进行优化;
④对于工具类只封装好了1.注册驱动2.创建连接8.回收资源,因此3.4.5.6.7.这五步没有完成;因此需要用高级应用层封装对这五步进行封装;基本每一个数据表都有一个对应的DAO接口以及其实现类,对其进行增删改查,但是这些操作重复性很高,所以可以抽取出公共的代码,然后给这些DAO的实现类可以抽取一个公共的父类,称为BaseDao; 对于查询操作需要用executeQuery,增删改操作需要用executeUpdate,所以增删改一体,查询一体;
后面会继续更新Mybatis简化JDBC的操作;
以下代码可以直接复制到idea中运行,整体的位置如下:(注意导入druid以及jdbc jar包)
代码一:转账的练习
包含两部分代码,一部分是Service层一部分是Dao层;
package data_test7;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;//TODO 此类是对bank表的一些操作;
public class BankDao {//account 加钱的账号,money:加钱的金额;这里需要设计jdbc因为是对数据库中的表中数据进行操作:public void add(String account,int money,Connection connection)throws Exception{//此处就不需要再创建链接了,为了保证同一个事务,需要使用一样的连接才行;//Class.forName("com.mysql.cj.jdbc.Driver");//Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jdbc_test", "root", "dir99");String sql="update t_bank set money=money+? where account=?;";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setObject(1,money);preparedStatement.setObject(2,account);int i = preparedStatement.executeUpdate();preparedStatement.close();//connection.close();System.out.println("加钱成功!");}public void sub(String account,int money,Connection connection)throws Exception{//此处就不需要再创建链接了,为了保证同一个事务,需要使用一样的连接才行;//Class.forName("com.mysql.cj.jdbc.Driver");//Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jdbc_test", "root", "dir99");String sql="update t_bank set money=money-? where account=?;";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setObject(1,money);preparedStatement.setObject(2,account);int i = preparedStatement.executeUpdate();preparedStatement.close();//connection.close();System.out.println("扣钱成功!");}
}
package data_test7;import org.junit.Test;import java.sql.Connection;
import java.sql.DriverManager;//TODO 转账测试:
//TODO 银行卡业务方法,调用Dao中的方法;
public class BankService {@Testpublic void start() throws Exception {//hello给hi转账500块:transfer("hi","hello",500);}public void transfer(String addAccount,String subAccount,int money) throws Exception {BankDao bankDao=new BankDao();//注意这种方法不准确,因为当一个账户money为零的时候,再运行加钱还是成功,扣钱会报错。// 因此需要统一为一个事务,这个事务包括加钱和扣钱;注意一个事务最基本的要求就是必须是同一个连接对象,connection;//TODO 需要加上注册驱动和创建连接以及try-catch并且需要关闭自动提交事务,这样加钱和扣钱就是同一个事务;Class.forName("com.mysql.cj.jdbc.Driver");Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jdbc_test", "root", "dir99");try{//设置不自动提交事务connection.setAutoCommit(false);bankDao.add(addAccount,money,connection);//此处需要再传入connection连接;bankDao.sub(subAccount,money,connection);//事务提交:connection.commit();}catch(Exception e){//事务回滚:connection.rollback();//抛出异常:throw e;}finally {connection.close();}//TODO 正常就提交事务,出现异常就回滚到原来的那样,防止扣钱失败,但是加钱成功类似的错误;//TODO 总结:事务是添加到业务方法中的;利用try-catch代码块,开始事务和提交事务以及事务回滚;将connection传入dao层即可,dao只负责使用,不用close;}
}
代码二:连接池的使用:
package data_test8;//TODO 数据库连接池:每次使用连接就创建然后销毁的话,会比较浪费资源,因此使用的时候可以在连接池中直接获取,使用完后再放回到连接池中:
// Druid连接池的使用: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.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;public class DruidUsepart {//硬编码方式://直接使用代码设置连接池连接参数方式!//1.创建一个druid连接池对象//2.设置连接池参数:【必须|非必须】//3.获取连接[通用方法,所有连接都一样]//4.回收连接public void testHard() throws Exception {//连接池对象:DruidDataSource dataSource=new DruidDataSource();//设置参数://必须设置的参数:dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/jdbc_test");dataSource.setUsername("root");dataSource.setPassword("dir99");dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");//帮助注册驱动和创建连接;//非必须的参数:dataSource.setInitialSize(5);//初始化连接的数量;dataSource.setMaxActive(10);//最大数量//获取链接:Connection connection=dataSource.getConnection();//数据库操作connection.close();}public void testSoft() throws Exception {//软编码方式://通过读取外部的配置文件的方法实例化druid连接池对象;//1.读取配置文件 PropertiesProperties properties=new Properties();InputStream resourceAsStream = DruidUsepart.class.getClassLoader().getResourceAsStream("druid.properties");properties.load(resourceAsStream);//2.使用连接池工具类的工厂模式创建连接池;DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);Connection connection=dataSource.getConnection();//数据库操作connection.close();}
}
//TODO 每次使用连接都这样重新设置比较麻烦,因此想将他们封装到工具类中,每次想使用调用那个类即可;
代码三:工具类的实现以及优化:
普通版:
package data_test8;import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;//TODO 工具类1.0版本,内部包含一个连接池对象,并且对外提供获取连接和回收连接的方法;
//TODO 工具类中的方法一般设置为静态的,方便外部调用;
/*实现内容:
属性:连接池对象只能实例化一次 实现方法:单例模式或者static代码块(全局只调用一次)
方法:对外提供链接的方法,回收外部传入连接方法*/
public class jdbc_utils1 {private static DataSource dataSource=null;static{Properties properties=new Properties();InputStream resourceAsStream = jdbc_utils1.class.getClassLoader().getResourceAsStream("druid.properties");try {properties.load(resourceAsStream);} catch (IOException e) {e.printStackTrace();}try {dataSource= DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {e.printStackTrace();}}//对外提供获取连接方法:public static Connection getConnection() throws SQLException {return dataSource.getConnection();}//回收方法:public static void freeConnection(Connection connection) throws SQLException {connection.close();}}
优化后:
package data_test8;
import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
//TODO 对于第一个版本的工具类有缺陷,就是在不同方法中调用getconnection方法,返回的是新的连接,并不是一样的连接;
//TODO 利用线程本地变量存储连接信息,确保一个线程的多个方法获取同一个连接connection;
// 优点:实务操作的时候,service和dao 属于同一个线程,不用再传递参数了,大家都可以调用getConnection方法自动获取的是同一个连接;public class jdbc_util2 {private static DataSource dataSource=null;private static ThreadLocal<Connection>tl=new ThreadLocal<>();static{Properties properties=new Properties();InputStream resourceAsStream = jdbc_utils1.class.getClassLoader().getResourceAsStream("druid.properties");try {properties.load(resourceAsStream);} catch (IOException e) {e.printStackTrace();}try {dataSource= DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {e.printStackTrace();}}//对外提供获取连接方法:public static Connection getConnection() throws SQLException {//先查看线程本地变量中是否存在Connection connection = tl.get();if(connection==null){//线程本地变量中没有,连接池获取connection=dataSource.getConnection();//存入线程本地变量tl.set(connection);}return connection;}//回收方法:public static void freeConnection() throws SQLException {Connection connection=tl.get();if(connection!=null){//清空线程本地变量tl.remove();connection.setAutoCommit(true);//要回归到初始状态,当开启事务的时候是false;connection.close();//回收到连接池;}connection.close();}
}
代码四:对转账练习的优化(使用连接池以及工具类)
package data_test8.daoANDservice;import data_test8.jdbc_util2;import java.sql.Connection;
import java.sql.PreparedStatement;//TODO 此类是对bank表的一些操作;
public class BankDao {//account 加钱的账号,money:加钱的金额;这里需要设计jdbc因为是对数据库中的表中数据进行操作:public void add(String account,int money)throws Exception{//TODO 可以直接从连接池中获取:Connection connection = jdbc_util2.getConnection();String sql="update t_bank set money=money+? where account=?;";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setObject(1,money);preparedStatement.setObject(2,account);int i = preparedStatement.executeUpdate();preparedStatement.close();//connection.close();System.out.println("加钱成功!");}public void sub(String account,int money)throws Exception{
//TODO 直接从连接池中获取Connection connection = jdbc_util2.getConnection();String sql="update t_bank set money=money-? where account=?;";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setObject(1,money);preparedStatement.setObject(2,account);int i = preparedStatement.executeUpdate();preparedStatement.close();//connection.close();System.out.println("扣钱成功!");}
}
package data_test8.daoANDservice;import data_test8.jdbc_util2;
import org.junit.Test;import java.sql.Connection;
import java.sql.DriverManager;//TODO 转账测试:
//TODO 银行卡业务方法,调用Dao中的方法;
public class BankService {@Testpublic void start() throws Exception {//hello给hi转账500块:transfer("hi","hello",500);}public void transfer(String addAccount,String subAccount,int money) throws Exception {BankDao bankDao=new BankDao();//TODO 直接从连接池中获取Connection connection = jdbc_util2.getConnection();try{//设置不自动提交事务,开启事务:connection.setAutoCommit(false);bankDao.add(addAccount,money);bankDao.sub(subAccount,money);//事务提交:connection.commit();}catch(Exception e){//事务回滚:connection.rollback();//抛出异常:throw e;}finally {jdbc_util2.freeConnection();}}
}
代码五:对sql语句进行封装,结合工具类以及连接池对增删改查操作的优化;
package data_test9;//TODO 首先jdbc中一共有八步:1.注册驱动2.创建连接3.编写sql语句4创建statement5.占位符赋值6.发送sql语句7.结果解析8.回收资源;import data_test8.jdbc_util2;import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;// TODO 对于test8中的工具类只封装好了1.注册驱动2.创建连接8.回收资源,因此3.4.5.6.7.这五步没有完成;因此需要用高级应用层封装对这五步进行封装;
// TODO 基本每一个数据表都有一个对应的DAO接口以及其实现类,对其进行增删改查,但是这些操作重复性很高,所以可以抽取出公共的代码,然后给这些DAO的实现类可以抽取一个公共的父类,称为BaseDao;
//TODO 对于查询操作需要用executeQuery,增删改操作需要用executeUpdate,所以增删改一体,查询一体;
public class BaseDao {public int executeUpdate(String sql,Object...params) throws SQLException {//TODO 此处是对非查询语句方法的封装:sql是传入的带占位符的sql语句;params是占位符的值,此处用了可变参数(注意,可变参数可以直接当作数组使用);Connection connection = jdbc_util2.getConnection();PreparedStatement preparedStatement = connection.prepareStatement(sql);for(int i=0;i<params.length;i++){//TODO 注意此处preparedStatement.setObject(i+1,params[i]);}int rows = preparedStatement.executeUpdate();//是否回收连接,需要考虑是否是事务,如果将setAutoCommit设置为false代表事务开始,不自动提交,这一系列的操作,要么全部执行成功,要么全部都不成功;如果开启事务了,就不用管,业务层去处理;if(connection.getAutoCommit()){//可以通过这个方法来获取是否是false还是true也就是事务是否开启//如果为true代表没有开启事务,此时就需要回收;jdbc_util2.freeConnection();}return rows;}/*对于非查询语句的方法返回值是int类型,代表了影响的行数;但是对于查询语句方法返回的是什么类型呢,确实是一个集合,但并不是list<Map>,map没有数据校验机制,并且不支持反射操作;其实数据库数据-》对应java的实体类,有一个表:user表,里面有id,name,account,password属性,此时这个表对应一个java类:User类,有id,name,account,password属性,那么表中的一行数据,代表java类的一个对象,——》多行——》List<java实体类>list;java实体类可以校验并且支持反射操作;那么返回值的类型就是某一个实体类的集合<T>声明一个泛型,不确定类型;第一个<T>表示这个方法是一个泛型方法,可以用于指定查询结果的类型;List<T>表示该方法返回的是一个包含T类型的对象的集合,下方的Class<T>由外面传入,确定这个泛型的类型,例如传入一个User类,那么这个泛型就是User类型,还有一个好处就是可以使用这个类的反射机制给属性赋值public <T> List<T> executeQuery(Class<T>cla,String sql,Object...params)具体实现如下:*/public <T> List<T> executeQuery(Class<T> cla, String sql, Object...params) throws Exception {//获取连接:Connection connection = jdbc_util2.getConnection();PreparedStatement preparedStatement = connection.prepareStatement(sql);//占位符赋值:if(params!=null&¶ms.length!=0){for(int i=0;i<params.length;i++){preparedStatement.setObject(i+1,params[i]);}}//执行sql语句:ResultSet resultSet = preparedStatement.executeQuery();//结果解析:List<T>list=new ArrayList<>();ResultSetMetaData metaData = resultSet.getMetaData();//获取列的数量,也就是属性的数量;int columnCount = metaData.getColumnCount();while(resultSet.next()){T t=cla.newInstance();//利用反射调用类的无参构造函数实例化对象!for(int i=1;i<=columnCount;i++){//得到本行i列属性的值Object object=resultSet.getObject(i);//得到本行i列的属性名:String columnLabel = metaData.getColumnLabel(i);//此时得到了这一列属性名和属性值,也就是给这个类的实例化对象的属性赋值,可以利用反射实现:Field field = cla.getDeclaredField(columnLabel);field.setAccessible(true);//属性可能是私用的,这样就可以打破private修饰限制,属性可以被设置;//给对象的属性赋值:第一个参数是想要赋值的对象,如果属性为静态的,可以为null;第二个参数是属性值:field.set(t,object);}list.add(t);}//关闭资源:resultSet.close();preparedStatement.close();if(connection.getAutoCommit()){//没有事务可以关闭:jdbc_util2.freeConnection();}return list;}}
package data_test9;import org.junit.Test;import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//TODO 利用新封装的BaseDao类,对增删改查进行优化:
public class better extends BaseDao {//TODO 此处需要继承一下BaseDao这样就有了其中的Update方法;public static void main(String[] args) {}@Testpublic void testInsert() throws Exception {
// //对于job_grades表:添加A 1500 3000这条数据
// //1.创建驱动:
// Class.forName("com.mysql.jdbc.Driver");
// //2.创建连接:
// Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb","root","dir99");
// //3.编写sql语句以及创建prepareStatement
// String sql="insert into job_grades values(?,?,?)";
// PreparedStatement preparedStatement = connection.prepareStatement(sql);
// preparedStatement.setObject(1,"A");
// preparedStatement.setObject(2,1500);
// preparedStatement.setObject(3,3000);
// //发送sql语句:
// int i = preparedStatement.executeUpdate();
// if(i>0){
// System.out.println("数据插入成功!");
// }else{
// System.out.println("数据插入失败!");
// }
// preparedStatement.close();
// connection.close();String sql="insert into job_grades values(?,?,?)";int a = executeUpdate(sql, "A", 88888, 66666);//返回影响行数;}@Testpublic void testDelete()throws Exception{
// Class.forName("com.mysql.jdbc.Driver");
// Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb", "root", "dir99");
// String sql="delete from job_grades where grade_level=?";
// PreparedStatement preparedStatement = connection.prepareStatement(sql);
// preparedStatement.setObject(1,"F");
// int i = preparedStatement.executeUpdate();
// if(i>0){
// System.out.println("删除成功!");
// }else{
// System.out.println("删除失败!");
// }
// preparedStatement.close();
//String sql="delete from job_grades where grade_level=? and lowest_sal=?";executeUpdate(sql,"A",88888);}@Testpublic void testUpdate()throws Exception{//对于job_grades表:将刚添加的A 1500 3000这条数据中3000改为9999;
// Class.forName("com.mysql.jdbc.Driver");
// Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb", "root", "dir99");
// String sql="update job_grades set highest_sal=? where lowest_sal=?";
// PreparedStatement preparedStatement = connection.prepareStatement(sql);
// preparedStatement.setObject(1,9999);
// preparedStatement.setObject(2,1500);
// int i = preparedStatement.executeUpdate();
// if(i>0){
// System.out.println("更新成功!");
// }else{
// System.out.println("更新失败!");
// }
// preparedStatement.close();
// connection.close();String sql="update job_grades set highest_sal=? where lowest_sal=?";int i = executeUpdate(sql,99999,66666);}@Test//注意此处查询想查询所有数据,然后将数据放入到List<Map>list集合中://可知查询结果是一行一行的,返回的是resultSet,然后将一行存入到map中,map(key=列名,value=列的内容)-》List<Map> list;//实现思路:遍历每一行数据,一行对应一个map,获取一行的列名和对应的列的属性,装配即可;然后将map装到一个集合当中就完成了;public void testSearch()throws Exception{
// Class.forName("com.mysql.jdbc.Driver");
// Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb", "root", "dir99");
// String sql="select * from job_grades";
// PreparedStatement preparedStatement = connection.prepareStatement(sql);
// ResultSet resultSet = preparedStatement.executeQuery();
// //TODO 获取列的信息对象:避免手动获取每一个列的数据,metaData装的是当前结果集中列的信息对象(它可以根据下角标获取列的名称,也可以获取列的数量);
// ResultSetMetaData metaData = resultSet.getMetaData();
// //列的数量:有了它以后就可以水平遍历列:
// int columnCount = metaData.getColumnCount();
// List<Map> list=new ArrayList<>();
// while(resultSet.next()){
// //一行对应一个map;
// Map map=new HashMap();
// //下面这种方式纯手动提取,如果列的个数更多会非常麻烦并且换一个表就得重新写,效率很低;map.put("gradelevel",resultSet.getString(1));map.put("lowestsal",resultSet.getInt(2));map.put("highestsal",resultSet.getInt(3));
// //新的方法读取每一个属性的数据,自动遍历列,注意要从一开始,和数据区分开
// for(int i=1;i<=columnCount;i++){
// //获取对应列的属性值:
// Object value = resultSet.getObject(i);
// //要是想加入map中,需要传入key和value,value已经有了,但是key:列名还没有:获取列的名称:这个方法可以获取列的别名,getcolumnname方法会获取列的名称,万一要是起了别名,就找不到了;
// String columnLabel = metaData.getColumnLabel(i);
// map.put(columnLabel,value);
// }
// list.add(map);
// }
// System.out.println(list);
// for(Object data:list){
// System.out.println(data);
// }
// resultSet.close();
// preparedStatement.close();
// connection.close();String sql="select * from job_grades";Class<Job_grades> clas = Job_grades.class;List<Job_grades> a = executeQuery(clas, sql);for (Object o:a){System.out.println(o);}}
}
class Job_grades{private String grade_level;private int lowest_sal;private int highest_sal;public String toString(){return grade_level+" "+lowest_sal+" "+highest_sal;}}
package data_test9;import org.junit.Test;import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//TODO 利用新封装的BaseDao类,对增删改查进行优化:
public class better extends BaseDao {//TODO 此处需要继承一下BaseDao这样就有了其中的Update方法;public static void main(String[] args) {}@Testpublic void testInsert() throws Exception {
// //对于job_grades表:添加A 1500 3000这条数据
// //1.创建驱动:
// Class.forName("com.mysql.jdbc.Driver");
// //2.创建连接:
// Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb","root","dir99");
// //3.编写sql语句以及创建prepareStatement
// String sql="insert into job_grades values(?,?,?)";
// PreparedStatement preparedStatement = connection.prepareStatement(sql);
// preparedStatement.setObject(1,"A");
// preparedStatement.setObject(2,1500);
// preparedStatement.setObject(3,3000);
// //发送sql语句:
// int i = preparedStatement.executeUpdate();
// if(i>0){
// System.out.println("数据插入成功!");
// }else{
// System.out.println("数据插入失败!");
// }
// preparedStatement.close();
// connection.close();String sql="insert into job_grades values(?,?,?)";int a = executeUpdate(sql, "A", 88888, 66666);//返回影响行数;}@Testpublic void testDelete()throws Exception{
// Class.forName("com.mysql.jdbc.Driver");
// Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb", "root", "dir99");
// String sql="delete from job_grades where grade_level=?";
// PreparedStatement preparedStatement = connection.prepareStatement(sql);
// preparedStatement.setObject(1,"F");
// int i = preparedStatement.executeUpdate();
// if(i>0){
// System.out.println("删除成功!");
// }else{
// System.out.println("删除失败!");
// }
// preparedStatement.close();
//String sql="delete from job_grades where grade_level=? and lowest_sal=?";executeUpdate(sql,"A",88888);}@Testpublic void testUpdate()throws Exception{//对于job_grades表:将刚添加的A 1500 3000这条数据中3000改为9999;
// Class.forName("com.mysql.jdbc.Driver");
// Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb", "root", "dir99");
// String sql="update job_grades set highest_sal=? where lowest_sal=?";
// PreparedStatement preparedStatement = connection.prepareStatement(sql);
// preparedStatement.setObject(1,9999);
// preparedStatement.setObject(2,1500);
// int i = preparedStatement.executeUpdate();
// if(i>0){
// System.out.println("更新成功!");
// }else{
// System.out.println("更新失败!");
// }
// preparedStatement.close();
// connection.close();String sql="update job_grades set highest_sal=? where lowest_sal=?";int i = executeUpdate(sql,99999,66666);}@Test//注意此处查询想查询所有数据,然后将数据放入到List<Map>list集合中://可知查询结果是一行一行的,返回的是resultSet,然后将一行存入到map中,map(key=列名,value=列的内容)-》List<Map> list;//实现思路:遍历每一行数据,一行对应一个map,获取一行的列名和对应的列的属性,装配即可;然后将map装到一个集合当中就完成了;public void testSearch()throws Exception{
// Class.forName("com.mysql.jdbc.Driver");
// Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atguigudb", "root", "dir99");
// String sql="select * from job_grades";
// PreparedStatement preparedStatement = connection.prepareStatement(sql);
// ResultSet resultSet = preparedStatement.executeQuery();
// //TODO 获取列的信息对象:避免手动获取每一个列的数据,metaData装的是当前结果集中列的信息对象(它可以根据下角标获取列的名称,也可以获取列的数量);
// ResultSetMetaData metaData = resultSet.getMetaData();
// //列的数量:有了它以后就可以水平遍历列:
// int columnCount = metaData.getColumnCount();
// List<Map> list=new ArrayList<>();
// while(resultSet.next()){
// //一行对应一个map;
// Map map=new HashMap();
// //下面这种方式纯手动提取,如果列的个数更多会非常麻烦并且换一个表就得重新写,效率很低;map.put("gradelevel",resultSet.getString(1));map.put("lowestsal",resultSet.getInt(2));map.put("highestsal",resultSet.getInt(3));
// //新的方法读取每一个属性的数据,自动遍历列,注意要从一开始,和数据区分开
// for(int i=1;i<=columnCount;i++){
// //获取对应列的属性值:
// Object value = resultSet.getObject(i);
// //要是想加入map中,需要传入key和value,value已经有了,但是key:列名还没有:获取列的名称:这个方法可以获取列的别名,getcolumnname方法会获取列的名称,万一要是起了别名,就找不到了;
// String columnLabel = metaData.getColumnLabel(i);
// map.put(columnLabel,value);
// }
// list.add(map);
// }
// System.out.println(list);
// for(Object data:list){
// System.out.println(data);
// }
// resultSet.close();
// preparedStatement.close();
// connection.close();String sql="select * from job_grades";Class<Job_grades> clas = Job_grades.class;List<Job_grades> a = executeQuery(clas, sql);for (Object o:a){System.out.println(o);}}
}
class Job_grades{private String grade_level;private int lowest_sal;private int highest_sal;public String toString(){return grade_level+" "+lowest_sal+" "+highest_sal;}}
相关文章:

后端开发——JDBC的学习(三)
本篇继续对JDBC进行总结: ①通过Service层与Dao层实现转账的练习; ②重点:由于每次使用连接就手动创建连接,用完后就销毁,这样会导致资源浪费,因此引入连接池,练习连接池的使用; …...
Redis 生产环境查找无过期时间的 key
在项目中,Redis 不应该被当作传统数据库来使用;储存大量没有过期时间的数据。如果储存大量无过期时间,而且无效的key的话;再加上 Redis 本身的过期策略没有被正确设置,就会大量占用内存。这样就会导致再多的内存资源也不够用。 情况大致是这样,项目中采用 Redis 二级存储…...
Visual Studio 2017编译Python3.8.18源码
一直纠结Python的开发环境没有升级到最新版3.8.18。这是当前的最新版,正在用的版本3.8.10。他是官方制作出安装包的最新版。 1、准备Visual C 2017的开发环境包括但不限于使用C的桌面开发x64 2、在Python官网下载Python3.8.18的源码。 3、解压缩源码 4、进入控制…...

【mujoco】Ubuntu20.04中解决mujoco报错raise error.MujocoDependencyError
【mujoco】Ubuntu20.04中解决mujoco报错raise error.MujocoDependencyError 文章目录 【mujoco】Ubuntu20.04中解决mujoco报错raise error.MujocoDependencyError1. 报错的具体情况2. 解决过程3. 其他问题3.1 ModuleNotFoundError: No module named OpenGL3.2 ModuleNotFoundEr…...
机器学习的三个方面
1 机器学习的三个方面 1.1 数据 包括数据采集、增强和质量管理,相当于给人工智能模型学习什么样的知识 第一、什么专业的知识; 第二、知识是否有体系,也就是说样本之间是否存在某种关联、差异等,这个涉及到样本选择等问题&#x…...

关于一名资深Java程序员在移动端的进阶之路
今天呢,就借此机会,跟大家聊一聊我的个人职业经历吧! 那年刚毕业 刚毕业时候,入职的第一家公司,进去后,说实话,没有太大成长吧!基本就是让我做一些可有可无的边缘性的工作ÿ…...
clickonce excel 插件发布安装的原理
ClickOnce 是一种由 Microsoft 提供的部署技术,用于简化和加速Windows应用程序的部署。ClickOnce 可以用于部署各种类型的应用程序,包括 Excel 插件。 以下是 ClickOnce Excel 插件发布和安装的一般原理: 1. 发布应用程序: -…...

关于MySQL Cluster
目录 1.MySQL Cluster2.MySQL Cluster架构3.MySQL Cluster 与 MySQL 主从架构有什么区别4.参考 MySQL Cluster是MySQL的一个高可用性,高性能的分布式数据库解决方案。它结合了内存数据库和共享无状态架构的技术,提供了99.999%的可用性,满足严…...

牵绳遛狗你我他文明家园每一天,助力共建文明社区,基于YOLOv7开发构建公共场景下未牵绳遛狗检测识别系统
遛狗是每天要打卡的事情,狗狗生性活泼爱动,一天不遛就浑身难受,遛狗最重要的就是要拴绳了,牵紧文明绳是养犬人的必修课。外出遛狗时,主人手上的牵引绳更多是狗狗生命健康的一道重要屏障。每天的社区生活中,…...

命令行艺术:简洁指南,效率倍增 | 开源日报 No.136
jlevy/the-art-of-command-line Stars: 141.7k License: NOASSERTION 这个项目是关于命令行的艺术,它提供了一系列有用的笔记和技巧,涵盖了基础知识、日常使用、文件和数据处理以及系统调试等方面。该指南旨在帮助初学者和经验丰富的用户掌握 Bash 命令…...
python基础教程五(字典概念和基本操作)
需要将一些列值组合成数据结构并通过编号来访问各个值时,列表很有用。本章介绍一种通过名称来访问各个值的数据结构。这种数据结构称为映射。字典是python中唯一的内置映射类型,其中的值不按顺序排列,而是存储在键下。键可能是数,…...
【Delphi 基础知识 11】重载函数的使用
在Delphi中使用重载函数时,你可以创建多个具有相同名称但参数列表不同的函数。这样,编译器可以根据函数调用时提供的参数类型或数量来确定要调用的具体函数。以下是一个简单的重载函数的例子: unit OverloadExample;interfaceusesSysUtils;t…...

经典目标检测YOLO系列(一)YOLOV1的复现(1)总体架构
经典目标检测YOLO系列(一)实现YOLOV1网络(1)总体架构 实现原版的YOLOv1并没有多大的意义,因此,根据《YOLO目标检测》(ISBN:9787115627094)一书,在不脱离YOLOv1的大部分核心理念的前提下,重构一款较新的YOLOv1检测器,来…...

《设计模式》之策略模式
策略模式定义 比如对象的某个行为,在不同场景有不同实现方式,可以将这些行为的具体实现定义为一组策略,每个实现类实现种策略,在不同场景使用不同的实现,并且可以自由切换策略。 策略模式结构 策略模式需要一个策略…...
Django文章标签推荐
当博客文章实现了标签后,可以用它们做很多有趣的事情。关于标签的更多内容,请看 Django集成第三方标签功能-CSDN博客 使用标签,我们可以很好地对博客文章进行分类。类似主题的帖子会有几个共同的标签。下一步将构建一个功能,以显…...
Git、TortoiseGit进阶
1.安装Git、TortoiseGit和汉化包 Git官网: Git TortoiseGit和汉化包: Download – TortoiseGit – Windows Shell Interface to Git 2.常用命令 创建仓库命令 git init初始化仓库git clone拷贝一份远程仓库,也就是下载一个项目。提交与修改 git add添加文件到暂存区git…...

山区老人爱的礼物丨守护银龄,情暖寒冬
为让山区老人们在寒冷的冬天感受到来自社会的温暖,新年伊始,北京传益千里携手志愿者再次走进酉阳土家族苗族自治县木叶乡分发新的一轮山区老人爱的礼物,让更多的物资走向有需要的人群。 中午阳光正好,志愿者们走进山林中的人家&am…...

【计算机算法设计与分析】n皇后问题(C++_回溯法)
文章目录 题目描述测试样例算法原理算法实现参考资料 题目描述 在nxn格的棋盘上放置彼此不受攻击的n格皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于在nxn格的棋盘上放置n个皇后,任何2个皇后不放在同…...
Calendar日历类型常见方法
Calendar日历类型常见方法: 概括:1.get( )方法2、set( ) 设置时间3、常用的add方法4、after()方法表示的时间是否在指定时间之后, before( ) 方法则之前, 返回判断结果4.1、compareTo比较器 概括: Calendar类是一个抽…...

Docker-Compose部署Redis(v7.2)主从模式
文章目录 一、前提准备1. redis配置文件2. 下载redis镜像3. 文件夹结构 二、docker-compose三、主从配置1.主节点配置文件2.从节点配置文件 四、运行五、测试 环境 docker desktop for windows 4.23.0redis 7.2 一、前提准备 1. redis配置文件 因为Redis 7.2 docker镜像里面…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...