后端开发——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镜像里面…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

rknn toolkit2搭建和推理
安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 ,不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源(最常用) conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...

数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)
名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 原创笔记:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 上一篇:《数据结构第4章 数组和广义表》…...

UE5 音效系统
一.音效管理 音乐一般都是WAV,创建一个背景音乐类SoudClass,一个音效类SoundClass。所有的音乐都分为这两个类。再创建一个总音乐类,将上述两个作为它的子类。 接着我们创建一个音乐混合类SoundMix,将上述三个类翻入其中,通过它管理每个音乐…...

轻量级Docker管理工具Docker Switchboard
简介 什么是 Docker Switchboard ? Docker Switchboard 是一个轻量级的 Web 应用程序,用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器,使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...