赞
踩
JDBC是指数据库连接技术,用于java连接mySQL等数据库。本文详细介绍了尚硅谷课程中JDBC的学习内容和补充知识。
概述
六大基本步骤获取连接,包括直接输入字符串的Statement和改进版的PreparedStatement(通过占位符解决了容易SQL攻击的问题)
JDBC的增删改查,其中插入数据需要考虑主键自增长、批量插入效率低的问题
建立数据库事务(基本特征是关闭了自动提交,要同时完成多个操作后再一起提交,如转账过程先增加A账户的钱,再扣除B账户的钱,一定要一起提交,防止出现只成功一件事)
连接池,优化建立连接的过程(每件事都去连接很繁琐,浪费资源,Druid连接池)
- 硬编码:通过设置参数的方式
- 软编码:通过读取外部配置文件的方式(读取方法使用了类加载器,补充说明在末尾)
封装连接工具类(通过静态代码块的方式保证连接池只被创建一次,而不是每次调用方法都创建一个新的连接池对象。)
- V1.0:不同方法调用拿到的是不同连接对象
- V2.0:利用线程本地量的方法,保证同一个线程不同方法拿到的是同一个连接
完整工具类封装(连接池优化了建立连接,那么把增删改查操作也封装一下吧)
- executeUpdate(实现数据的更新操作,包括增,删,改)
- executeQuery(实现数据的查询操作,通过反射机制,输入模板对象,返回查询出的对象列表)
mySQL端 创建t_user表
create database atguigu;
use atguigu;
create table t_user(
id int primary key auto_increment comment '用户主键',
account varchar(20) not null unique comment '账号',
password varchar(64) not null comment '密码',
nickname varchar(20) not null comment '昵称');
insert into t_user(account,password,nickname) values('root','123456','经理'),('admin','666666','管理员');
java端获取数据
public class StatementQuery { public static void main(String[] args) throws SQLException { //1.注册驱动 DriverManager.registerDriver(new Driver());//8+驱动要选择带cj的 //2. 获取连接,需要数据库ip地址(127.0.0.1),数据库端口号(3306),账号(root),密码,连接数据库的名称(atguigu) //jdbc:数据库厂商名//ip:端口号/数据库名 Connection connection=DriverManager. getConnection("jdbc:mysql://127.0.0.1:3306/atguigu","root","mypassword"); //3. 创建statement Statement statement = connection.createStatement(); //4. 发送SQL语句 String sql="select * from t_user"; ResultSet resultSet = statement.executeQuery(sql); //5. 进行结果集更新 while(resultSet.next()){ int id = resultSet.getInt("id"); String account = resultSet.getString("account"); String password = resultSet.getString("password"); String nickname = resultSet.getString("nickname"); System.out.println(id+"--"+account+"--"+password+"--"+nickname); } //6. 关闭资源 resultSet.close(); statement.close(); connection.close(); } }
public class SatetementUserLoginPart { public static void main(String[] args) throws SQLException, ClassNotFoundException { //0. 获取用户输入信息 Scanner scanner = new Scanner(System.in); System.out.println("请输入账号"); String account = scanner.nextLine(); System.out.println("请输入密码"); String password = scanner.nextLine(); //1.注册驱动,有两种方案 /*方案一-------------- //DriverManager.registerDriver(new Driver());//会注册两次驱动,可以考虑仅触发静态代码块 //类加载机制:类记载时刻会触发静态代码块,可以通过new,静态方法,静态属性等触发 */ //方案二------------- Class.forName("com.mysql.cj.jdbc.Driver");//触发类加载,触发静态代码块的调用 //优点是字符串可以提取到外部的配置文件,便于更换数据驱动,会更加灵活 //2. 获取数据库连接,共有三种方法 //该方法是一个重载方法,允许开发者用不同的形式传入数据库连接的核心参数 //三个参数------------- Connection connection=DriverManager. getConnection("jdbc:mysql://127.0.0.1:3306/atguigu","root","mypassword"); //jdbc:mysql:///atguigu 如果本机和3306可以省略为/// //两个参数-------------- /* Properties info=new Properties(); info.put("user","root"); info.put("password","root"); DriverManager.getConnection("jdbc:mysql:///atguigu",info); //一个参数-------------- //jdbc:数据库厂商名//ip:端口号/数据库名?user=root&password=root DriverManager.getConnection("jdbc:mysql:///atguigu?user=root&password=root"); //其他可选信息,如果是sql8.0以后的版本可以省略serverTimezone=Asia/Shanghai&UseUnicode=true&characterEncoding=utf8&useSSL=true */ //3. 创建发送SQL语句的Statetment对象 Statement statement = connection.createStatement(); //4. 发送SQL语句 String sql="SELECT * FROM t_user WHERE account='"+account+"'AND PASSWORD='"+password+"';"; //executeUpdate用于更新数据,返回影响的函数,executeQuery负责返回查询结果,返回结果封装对象 ResultSet resultSet = statement.executeQuery(sql); //5.查询结果集解析,resultSet给出的是逐行的对象,使用next可以逐行提取下一个,遍历完毕时返回false /*仅查询的方案 while(resultSet.next()){ //光标已经移动,此时利用getInt\getString读取该行的数据 //getString输入可以是index(列的下角标,从左向右从1开始),可以是列名(有别名写别名) int id = resultSet.getInt("id"); String account1 = resultSet.getString("account"); String password1 = resultSet.getString("password"); String nickname = resultSet.getString("nickname"); System.out.println(id+"--"+account+"--"+password+"--"+nickname); } */ //考虑实际需求,只需要有一行数据证明就可以登录成功 if(resultSet.next()){ System.out.println("登录成功"); }else { System.out.println("登录失败,用户名或密码错误"); } //6. 关闭资源 resultSet.close(); statement.close(); connection.close(); } }
存在几个问题
SQL语句需要字符串拼接比较麻烦
只能拼接字符串类型,其他的数据库类型无法处理
可能发生注入攻击(动态值充当了SQL语句结构)
public class SPUserLoginPart { public static void main(String[] args) throws ClassNotFoundException, SQLException { //0. 获取用户输入信息 Scanner scanner = new Scanner(System.in); System.out.println("请输入账号"); String account = scanner.nextLine(); System.out.println("请输入密码"); String password = scanner.nextLine(); //1.注册驱动,有两种方案 Class.forName("com.mysql.cj.jdbc.Driver");//触发类加载,触发静态代码块的调用 //2. 获取数据库连接 Connection connection= DriverManager. getConnection("jdbc:mysql://127.0.0.1:3306/atguigu","root","mypassword"); //3. 创建发送SQL语句的preparedstatetment对象 //(1). 编写SQL语句结构,动态值部分使用占位符?替代(2).创建preparedstatetment,传入动态值 //(3). 动态值 占位符 赋值? 单独赋值即可 (4). 发送 String sql="SELECT * FROM t_user where account=? and password=?;"; PreparedStatement preparedStatement = connection.prepareStatement(sql); /* 参数一:index占位符的位置,从1开始 参数二:Object占位符的值,可以是任何类型 */ preparedStatement.setObject(1,account); preparedStatement.setObject(2,password); ResultSet resultSet = preparedStatement.executeQuery(); //5. 结果集解析 if(resultSet.next()){ System.out.println("登录成功"); }else { System.out.println("登录失败,用户名或密码错误"); } //6. 关闭资源 resultSet.close(); preparedStatement.close(); connection.close(); } }
public class PSCURDPart { @Test public void testInsert() throws ClassNotFoundException, SQLException { //1.注册驱动,有两种方案 Class.forName("com.mysql.cj.jdbc.Driver");//触发类加载,触发静态代码块的调用 //2. 获取数据库连接 Connection connection= DriverManager. getConnection("jdbc:mysql://127.0.0.1:3306/atguigu","root","mypassword"); //3. 创建发送SQL语句的preparedstatetment对象 String sql="insert into t_user(account,password,nickname) values(?,?,?)"; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setObject(1,"test"); preparedStatement.setObject(2,"test"); preparedStatement.setObject(3,"二狗"); int rows = preparedStatement.executeUpdate(); //5. 结果集解析 if(rows>0){ System.out.println("插入成功"); }else { System.out.println("插入失败"); } //6. 关闭资源 preparedStatement.close(); connection.close(); } @Test public void testUpdate() throws ClassNotFoundException, SQLException { //1.注册驱动,有两种方案 Class.forName("com.mysql.cj.jdbc.Driver");//触发类加载,触发静态代码块的调用 //2. 获取数据库连接 Connection connection= DriverManager. getConnection("jdbc:mysql://127.0.0.1:3306/atguigu","root","mypassword"); //3. 创建发送SQL语句的preparedstatetment对象 String sql="update t_user set nickname=? where id=?"; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setObject(1,"张三"); preparedStatement.setObject(2,3); int rows = preparedStatement.executeUpdate(); //5. 结果集解析 if(rows>0){ System.out.println("修改成功"); }else { System.out.println("修改失败"); } //6. 关闭资源 preparedStatement.close(); connection.close(); } @Test public void testDelete() throws ClassNotFoundException, SQLException { //1.注册驱动,有两种方案 Class.forName("com.mysql.cj.jdbc.Driver");//触发类加载,触发静态代码块的调用 //2. 获取数据库连接 Connection connection= DriverManager. getConnection("jdbc:mysql://127.0.0.1:3306/atguigu","root","mypassword"); //3. 创建发送SQL语句的preparedstatetment对象 String sql="delete from t_user where id=?"; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setObject(1,3); int rows = preparedStatement.executeUpdate(); //5. 结果集解析 if(rows>0){ System.out.println("删除成功"); }else { System.out.println("删除失败"); } //6. 关闭资源 preparedStatement.close(); connection.close(); } //查询所有用户数据,并封装到一个List<Map> 集合中,key=列名,value=列的内容 @Test public void testSelect() throws ClassNotFoundException, SQLException { //1.注册驱动,有两种方案 Class.forName("com.mysql.cj.jdbc.Driver");//触发类加载,触发静态代码块的调用 //2. 获取数据库连接 Connection connection= DriverManager. getConnection("jdbc:mysql://127.0.0.1:3306/atguigu","root","mypassword"); //3. 创建发送SQL语句的preparedstatetment对象 String sql="SELECT id,account,password,nickname FROM t_user;"; PreparedStatement preparedStatement = connection.prepareStatement(sql); ResultSet resultSet = preparedStatement.executeQuery(); //5. 结果集解析 List<Map> list=new ArrayList<>(); ResultSetMetaData metaData = resultSet.getMetaData();//获取当前结果集列的信息对象 int columnCount = metaData.getColumnCount();//为了水平遍历 while(resultSet.next()){ Map map=new HashMap(); for (int i = 1; i <columnCount ; i++) { Object value = resultSet.getObject(i); String columnLabel = metaData.getColumnLabel(i);//getlabel可以得到别名,name只能别名 map.put(columnLabel,value); } list.add(map); } System.out.println(list); //6. 关闭资源 preparedStatement.close(); connection.close(); } }
在多表关联插入数据时,一般主表的主键是自动生成的,所以插入数据前无法获取主键,但是从表需要再插入数据之前就绑定主表的主键,这时可以使用主键回显技术
public class PSOthePart { //t_user 插入一条数据,并获取数据库自增长的主键 @Test public void returnPrimaryKey() throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver"); Connection connection= DriverManager. getConnection("jdbc:mysql://127.0.0.1:3306/atguigu","root","mypassword"); String sql="Insert into t_user(account,password,nickname) values(?,?,?);"; //插入数据同时获取返回主键 PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); preparedStatement.setObject(1,"test1"); preparedStatement.setObject(2,"123456"); preparedStatement.setObject(3,"蛋"); int i=preparedStatement.executeUpdate(); if(i>0){ System.out.println("插入成功"); //获取主键的结果集对象,一行一列,id=值 ResultSet resultSet = preparedStatement.getGeneratedKeys(); resultSet.next(); int id=resultSet.getInt(1); System.out.println("id="+id); }else{ System.out.println("插入失败"); } preparedStatement.close(); connection.close(); } }
public void returnPrimaryKey2() throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver"); // 1.路径后面添加允许批量操作 Connection connection= DriverManager. getConnection("jdbc:mysql:///atguigu?rewriteBatchedStatements=true","root","mypassword"); String sql="Insert into t_user(account,password,nickname) values(?,?,?);"; PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); long start=System.currentTimeMillis(); for (int i = 1; i < 10000; i++) { preparedStatement.setObject(1,"dd"+i); preparedStatement.setObject(2,"dd"+i); preparedStatement.setObject(3,"蛋"+i); preparedStatement.addBatch();//2. 不执行,追加到values后面,批量添加addBatch } preparedStatement.executeBatch();//3. 执行批量操作 long end = System.currentTimeMillis(); preparedStatement.close(); connection.close(); }
允许在失败情况下,数据回归到业务之前的状态!
具有原子性(不可切分)、一致性(状态不变)、隔离性(互不干扰)、持久性(永久性改变)
手动提交
实现目标:实现两个账户之间的安全转账,如果一方余额不足不会出现转账错误(即一方账户增加了另一方没有减少)
CREATE TABLE t_bank(
id INT PRIMARY KEY AUTO_INCREMENT,
account VARCHAR(20) NOT NULL UNIQUE COMMENT '账户',
money INT UNSIGNED COMMENT '金额,不能为负值'
);
INSERT INTO t_bank(account,money) VALUES
('ergouz1',1000),('lvdandan',1000);
public class BankService { @Test public void start() throws SQLException, ClassNotFoundException { transfer("lvdandan","ergouz1",500); } public void transfer(String addAccount,String subAccount,int money) throws SQLException, ClassNotFoundException { Class.forName("com.mysql.cj.jdbc.Driver"); // 1.路径后面添加允许批量操作 Connection connection= DriverManager. getConnection("jdbc:mysql:///atguigu","root","mypassword"); BankDao bankDao=new BankDao(); try{ connection.setAutoCommit(false);//关闭自动提交,开启手动提交 bankDao.add(addAccount,money,connection); System.out.println("----"); bankDao.sub(subAccount,money,connection); connection.commit();//事务提交 }catch (Exception e){ connection.rollback(); throw e; }finally { connection.close(); } } }
public class BankDao { public void add(String account,int money,Connection connection) throws ClassNotFoundException, SQLException { String sql="Update t_bank set money=money+? where account=?;"; PreparedStatement statement = connection.prepareStatement(sql); statement.setObject(1,money); statement.setObject(2,account); statement.executeUpdate(); statement.close(); } public void sub(String account,int money,Connection connection) throws ClassNotFoundException, SQLException { String sql="Update t_bank set money=money-? where account=?;"; PreparedStatement statement = connection.prepareStatement(sql); statement.setObject(1,money); statement.setObject(2,account); statement.executeUpdate(); statement.close(); } }
public class DruidUsePart { //直接使用代码设置连接池连接参数等方式 @Test public void testHard() throws SQLException { DruidDataSource dataSource=new DruidDataSource(); //连接数据库驱动类的全限定符 注册驱动 dataSource.setUrl("jdbc:mysql:///atguigu"); dataSource.setUsername("root"); dataSource.setPassword("mypassword"); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setInitialSize(5);//初始化连接数量 dataSource.setMaxActive(10);//最大的数量 DruidPooledConnection connection = dataSource.getConnection();//获取连接 //数据库操作 // connection.close(); } //读取外部配置文件的方法实例化连接对象 public void testSoft() throws Exception { Properties properties = new Properties(); InputStream ips = DruidUsePart.class.getClassLoader().getResourceAsStream("druid.properties"); properties.load(ips); DataSource dataSource = DruidDataSourceFactory.createDataSource(properties); Connection connection = dataSource.getConnection(); //数据库操作 // connection.close(); } }
druid.properties
#key = value => java properties读取
#druid的key必须固定命名
driverClassName=com.mysql.cj.jdbc.Driver
username=root
password=123456
url=jdbc:mysql://127.0.0.1:3306/atguigu
通过静态代码块的方式保证连接池只被创建一次,而不是每次调用方法都创建一个新的连接池对象
public class JdbcUtils { private static DataSource dataSource = null;//连接池对象 static{ //初始化连接池对象 Properties properties = new Properties(); InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties"); try { properties.load(is); } 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();//连接池的连接执行回收 } }
问题:不同方法调用拿到的是不同对象,考虑如何同一个线程不同方法拿到的是同一个连接
利用线程本地变量,存储连接信息 确保一个线程的多个方法可以获取同一个connection
优势:事务操作的时候server 和 dao 属于同一个线程,不用再传递参数
大家都可以调用getConnection自动获取的是相同的连接池
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
V2版本代码
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 JdbcUtilsV2 { private static DataSource dataSource = null;//连接池对象 private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>(); static{ //初始化连接池对象 Properties properties = new Properties(); InputStream is = JdbcUtilsV2.class.getClassLoader().getResourceAsStream("druid.properties"); try { properties.load(is); } 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 { //先查看线程本地变量中是否存在 Connection connection = threadLocal.get(); if(connection == null){ //线程本地变量没有,连接池获取 connection = dataSource.getConnection(); threadLocal.set(connection); } return dataSource.getConnection(); } public static void freeConnection() throws SQLException { Connection connection = threadLocal.get(); if(connection != null){ threadLocal.remove();//清空本地变量数据 connection.setAutoCommit(true); connection.close();//连接池的连接执行回收 } } }
此时的封装只针对了获取连接部分,还不够完善,可以创建一个更加完善的工具类,包括数据库的增删改查
package utils; import java.lang.reflect.Field; import java.sql.*; import java.util.ArrayList; import java.util.List; /** * Description:封装dao层数据库重复代码 * TODO:封装两个方法 * 一个简化非DQL * 一个简化DQL(查询语句) */ public class BaseDao { /** * 封装简化非DQL语句 * @param sql 带占位符的SQL语句 * @param params 占位符的值 注意:传入的占位符的值,必须等于SQL语句?位置的值 * @return 执行影响的行数 */ public int executeUpdate(String sql, Object... params) throws SQLException {//Object...可变参数传参数形式,在代码中可以作为数组使用 //获取连接 Connection connection = JdbcUtilsV2.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(sql); //可变参数可当作数组使用 for (int i = 1; i <= params.length; i++) { preparedStatement.setObject(i+1, params[i-1]); } int rows = preparedStatement.executeUpdate(); preparedStatement.close(); //是否回收连接,需要考虑是不是事务,如果不是事务,则直接回收连接 if (connection.getAutoCommit()) { //true -> 没有开启事物,正常回收连接 JdbcUtilsV2.freeConnection(); } return rows; } /** * 将查结果封装到一个实体类集合 * @param clazz 要接值的实体类集合的模板对象 * @param sql 查询语句,要求类名或者别名等于实体类的属性名 u_id as uId => uId * @param params 占位符的值 要和 ? 位置对象对应 * @return 查询的实体类集合 * @param <T> 声明的结果的泛型 * @throws SQLException * @throws InstantiationException * @throws IllegalAccessException * @throws NoSuchFieldException */ //第一个<T>是方法泛型,需要调用时确定 // 第三个T是指使用反射技术赋值 public <T> List<T> executeQuery(Class<T> clazz,String sql,Object... params) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException { Connection connection = JdbcUtilsV2.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(sql); if(params != null && params.length != 0) { for (int i = 1; i <= params.length; i++) { preparedStatement.setObject(i,params[i-1]); } } ResultSet resultSet = preparedStatement.executeQuery(); List<T> list = new ArrayList<>(); //获取列的信息 //TODO:metadata装的是列的信息(可以获取列的名称根据下角标,可以获取列的数量) ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); while (resultSet.next()) { T t = clazz.newInstance();//调用类的无参构造函数实例化对象 //自动遍历列 注意:要从1开始,等于总列数 for (int i = 1; i <= columnCount; i++) { //获取指定列下角标的值 Object value = resultSet.getObject(i); //获取指定列下角标列的名称 //getColumnLabel: 会获取别名,如果没有定义别名,获取列名 不要使用getColumnName:只会获取列名 String key = metaData.getColumnLabel(i); //反射,给对象的属性值赋值 Field field = clazz.getDeclaredField(key);//获取属性key field.setAccessible(true);//属性可以设置,打破private限制 field.set(t,value);//给对象赋值(赋值对象,属性值),如果是静态属性,第一个参数可以为null } list.add(t); } resultSet.close(); preparedStatement.close(); if(connection.getAutoCommit()){ JdbcUtilsV2.freeConnection(); } return list; } }
package utils; import org.junit.Test; import java.util.List; /** * @create 2024-03-{DAY}-{TIME} */ public class JdbcCurdPart extends BaseDao { @Test public void testInsert() throws Exception { /** * t_user表插入一条数据 * account test * password test * nickname 二狗子 */ String sql = "insert into t_user(account,password,nickname) values(?,?,?)"; int i = executeUpdate(sql, "测试333", "333", "ergouzi"); if (i > 0) { System.out.println("插入成功"); } else { System.out.println("插入失败"); } } @Test public void testUpdate() throws Exception { String sql = "update t_user set nickname = ? where id = ?;"; executeUpdate(sql, "新的nickname", "4"); } @Test public void testDelete() throws Exception { String sql = "delete from t_user where id = ?;"; executeUpdate(sql, 4); } @Test public void testSelect() throws Exception { String sql = "select id,account,money from t_bank;"; List<Bank> banks = executeQuery(Bank.class, sql); for (Bank bank : banks) { System.out.println(bank); } } @Test public void testSelect1() throws Exception { String sql = "select * from t_bank;"; executeQuery(Bank.class,sql); } } class Bank{ int id; String account; int money; }
这部分内容用于反射、泛型不熟练的进一步理解代码
目的是执行类的静态代码块
使用 Class.forName("com.mysql.cj.jdbc.Driver")
语句是为了加载MySQL JDBC驱动程序。在Java中,类的加载是通过类加载器(ClassLoader)来完成的,Class.forName
方法是一种触发类加载的方式。这种加载方式主要用于加载类的静态代码块。
Class.forName
允许在运行时根据需要加载特定的驱动程序类。这使得你可以在不修改代码的情况下,通过配置文件或其他方式动态更改要使用的数据库驱动。总之,Class.forName("com.mysql.cj.jdbc.Driver")
的目的是为了加载并注册MySQL数据库驱动程序,使用反射的方式允许在运行时动态选择和加载驱动程序,提高了代码的灵活性和可配置性。
Class
对象。它是在编译时就被解析的,因此具有更好的类型安全性。类字面常量的语法形式为在类名后面添加 .class
。以下是类字面常量的示例:
javaClass<MyClass> myClass = MyClass.class;
这里,MyClass.class
表示 MyClass
类的 Class
对象。与使用 Class.forName()
方法不同,类字面常量在编译时就会受到检查,如果类名不存在或有语法错误,编译器将会报错。
这种方式的优点包括:
总体而言,如果在编译时就知道要加载的类,而且希望代码更加类型安全,类字面常量是一个更好的选择。如果类名是在运行时动态确定的,那么可以考虑使用 Class.forName()
方法。
InputStream ips = DruidUsePart.class.getClassLoader().getResourceAsStream("druid.properties");
这行代码是通过类加载器(ClassLoader)获取资源文件的输入流。这种方式通常用于从类路径中加载配置文件。让我解释一下为什么这样获取配置文件是常见的做法:
getResourceAsStream
方法是 ClassLoader
类的一个方法,它返回指定资源的输入流。这使得你可以直接使用输入流来读取资源,而不需要关心其具体的物理路径。在你的例子中,通过 DruidUsePart.class.getClassLoader().getResourceAsStream("druid.properties")
获取了名为 “druid.properties” 的配置文件的输入流。
public <T> List<T> executeQuery(Class<T> clazz, String sql, Object... params)
<T>
表示该方法是泛型的,返回类型为 List<T>
,其中 T
是在调用时确定的类型参数。这意味着,调用该方法时,需要提供一个具体的类型,例如 Bank.class
,以告诉方法返回什么类型的对象列表。Class<T> clazz
:这个参数表示需要执行查询的对象类型。通过传入 clazz
参数,方法可以了解需要构建的对象类型,并使用反射机制来创建对象或者设置对象的属性。Object... params
:这个参数是可变参数,用于传递查询中需要的参数。如何通过反射获取模板类的属性呢?
Field field = clazz.getDeclaredField(key);//获取属性key
field.setAccessible(true);//属性可以设置,打破private限制
field.set(t,value);//给对象赋值(赋值对象,属性值),如果是静态属性,第一个参数可以为null
Field field = clazz.getDeclaredField(key)
:这行代码使用了反射技术。clazz.getDeclaredField(key)
用于获取指定名称的字段。在这里,key
应该是你查询中的一个列名或者属性名。field.setAccessible(true)
:这行代码打破了字段的封装性,使得即使字段是私有的,也可以通过反射来访问或者修改它的值。field.set(t, value)
:这行代码将指定对象 t
的属性 field
的值设置为 value
。这里的 t
是一个对象实例,value
是要设置的值。Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。