Java--JDBC 谁践踏了优雅 2023-10-17 16:31 9阅读 0赞 **JDBC:java database connectivity(**Java数据库连接**)** SUN公司提供的一套操作数据库的标准规范;它是Java编程语言和广泛的数据库之间独立于数据库的连接标准的Java API,根本上说JDBC是一种规范,它提供一套完整的接口,允许便捷式访问底层数据库管理系统(DBMS),如不同生产产商的数据库管理系统 Mysql、Oracle、SQL Server等 如下图所示: ![watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBATWluZ2dlUWluZ2NodW4_size_7_color_FFFFFF_t_70_g_se_x_16][] JDBC与数据库驱动的关系:接口与实现的关系 JDBC四个核心对象: DriverManager:用于注册驱动 Connection:表示与数据库创建的连接 Statement:操作数据库sql语句的对象 ResultSet:结果集或一张虚拟表 1、在JDBC开发前,先从对应的数据库管理系统官网下载对应的驱动jar包 (1)对于文本编辑器的方式开发,需要将其配置到 环境变量classpath 当中,以mysql为例 classpath=.;D:\\course\\06-JDBC\\resources\\MySql Connector Java 5.1.XX\\mysql-connector-java-5.1.23-bin.jar (2)对于IDEA开发工具,需要导包 选中相应模块,右击,选择 Open Module Settings ![watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBATWluZ2dlUWluZ2NodW4_size_10_color_FFFFFF_t_70_g_se_x_16][] 选择 Libraries ,选择 Java ![watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBATWluZ2dlUWluZ2NodW4_size_7_color_FFFFFF_t_70_g_se_x_16 1][] 选择自己放置相应数据库管理系统的 驱动 jar 包,点击 Apply ,选择 OK即可 ![watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBATWluZ2dlUWluZ2NodW4_size_15_color_FFFFFF_t_70_g_se_x_16][] 2、Java中JDBC编程主要分为六步 (1)注册驱动(作用:告诉Java程序,即将要连接的是哪个品牌的数据库) (2)获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭通道。) (3)获取数据库操作对象(专门执行sql语句的对象) (4)执行SQL语句(DQL; DML....) (5)处理查询结果集(只有当第四步执行select语句时,才会有这第五步处理查询结果集) (6)释放资源(使用完资源之后一定要关闭资源,从小到大关闭,先关闭结果集ResultSet,再关闭操作数据库对象Statement,最后关闭连接Connection) #### 一、注册驱动 #### 1、DriverManager.registerDriver 注册 //1、注册驱动(连接的数据库);导入数据库的驱动jar包,如mysql;mysql-connector-java-8.0.27 //mysql驱动 Driver driver = new com.mysql.jdbc.Driver(); //Oracle驱动 // Driver driver = new oracle.jdbc.driver.OracleDriver(); DriverManager.registerDriver(driver); /* mysql的URL:jdbc:mysql://127.0.0.1:3306/数据库名 Oracle的URL:jdbc:oracle:thin:@localhost:1521:数据库名 Sql Server的URL:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=数据库名 * */ String url = "jdbc:mysql://127.0.0.1:3306/mydb"; String user = "root"; String pwd = "123456"; conn = DriverManager.getConnection(url,user,pwd); 一般不建议采用此种方式 (1)查看Driver的源代码可以看到,采用此种方式,会导致驱动程序注册两次,在内存中会有两个Driver对象 (2)程序依赖数据库管理系统厂商的api,如是mysql就需要注册mysql驱动,同理Oracle需要注册Oracle驱动,扩展性不高 2、类加载 Class.forName("com.mysql.jdbc.Driver"); //2、获取连接(表示JVM的进程和数据库进程之间的通道打开,属于进程之间的通信) String url = "jdbc:mysql://127.0.0.1:3306/mydb"; String user = "root"; String pwd = "123456"; conn = DriverManager.getConnection(url,user,pwd); 以mysql为例,查看com.mysql.jdbc.Driver源码,也会调用DriverManager.registerDriver() 方法 //类加载会调用静态代码块 com.mysql.jdbc.Driver中静态代码块会被调用 static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } } 3、数据库的URL URL用于标识数据库的位置,通过URL地址告诉JDBC程序连接哪个数据库,URL的写法为: jdbc:mysql://127.0.0.1:3306/数据库名 URL:统一资源定位符(网络中某个资源的绝对路径) URL中包括: 协议 IP PORT 资源名 > 如百度URL:http://182.61.200.7:80/index.html > > http:// 通信协议(通信协议 通信之前提前定好的数据传送格式) > > 182.61.200.7 服务器IP地址 > > 80 软件端口 > > index.html 服务器上某个资源名 常用数据库URL如下: mysql的URL:jdbc:mysql://127.0.0.1:3306/数据库名 Oracle的URL:jdbc:oracle:thin:@localhost:1521:数据库名 Sql Server的URL:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=数据库名 4、属性配置文件 可以将数据库驱动,和URL以及用户密码放在配置文件中,这样修改了数据库信息不需要重新编译Java程序 我们新建一个jdbc.properties属性配置文件,如下: driver=com.mysql.cj.jdbc.Driver dburl=jdbc:mysql://127.0.0.1:3306/mydb user=root pwd=123456 获取如下: //资源绑定器加载属性配置文件;不能带 .properties ResourceBundle bundle = ResourceBundle.getBundle("jdbc"); String driver = bundle.getString("driver"); String url = bundle.getString("dburl"); String user = bundle.getString("user"); String pwd = bundle.getString("pwd"); Connection conn = null; Statement stmt = null; //1、注册驱动(连接的数据库) try { Class.forName(driver); //2、获取连接 conn = DriverManager.getConnection(url,user,pwd); } 5、结果集处理 (1)执行DML语句(insert;update;delete);主要使用Statement 的executeUpdate()方法 public static void main(String[] args) { //资源绑定器加载属性配置文件;不能带 .properties ResourceBundle bundle = ResourceBundle.getBundle("jdbc/jdbc"); String driver = bundle.getString("driver"); String url = bundle.getString("dburl"); String user = bundle.getString("user"); String pwd = bundle.getString("pwd"); Connection conn = null; Statement stmt = null; //1、注册驱动(连接的数据库) try { Class.forName(driver); //2、获取连接 conn = DriverManager.getConnection(url,user,pwd); //3、获取数据库操作对象(专门执行sql语句的对象) stmt = conn.createStatement(); //4、执行SQL语句(DQL DML....) String sql = "update dept2 set deptname = 'rh' where deptno = 50"; int count = stmt.executeUpdate(sql); System.out.println("执行结果条数:" + count); //5、处理查询结果集(只有当第四步执行的是select语句的时候,才有处理查询结果集) } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { //6、释放资源;从小到大关闭 if (stmt != null) { try { stmt.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } } (2)执行DQL语句(select);主要使用Statement 的executeQuery()方法 public static void main(String[] args) { //资源绑定器加载属性配置文件;不能带 .properties ResourceBundle bundle = ResourceBundle.getBundle("jdbc/jdbc"); String driver = bundle.getString("driver"); String url = bundle.getString("dburl"); String user = bundle.getString("user"); String pwd = bundle.getString("pwd"); Connection conn = null; Statement stmt = null; ResultSet rs = null; try { //1、注册驱动(连接的数据库) Class.forName(driver); //2、获取连接 conn = DriverManager.getConnection(url,user,pwd); //3、获取数据库操作对象(专门执行sql语句的对象) stmt = conn.createStatement(); //4、执行SQL语句(DQL DML....) String sql = "select empno,empname as name,sal from emp2 where empno=7369"; rs = stmt.executeQuery(sql); //5、处理查询结果集(只有当第四步执行的是select语句的时候,才有处理查询结果集) while (rs.next()){ /* (1)下标取值;下标从 1 开始 */ String empno = rs.getString(1); String empname = rs.getString(2); String sal = rs.getString(3); System.out.println(empno + " " + empname + " " + sal); /* (2)数据类型取值 */ int empno1 = rs.getInt(1); String empname1 = rs.getString(2); Double sal1 = rs.getDouble(3); System.out.println(empno1 + " " + empname1 + " " + sal1); /* (3)字段名取值 */ String empno2 = rs.getString("empno"); //如果执行的SQL语句中有别名,需要使用别名字段取值 String empname2 = rs.getString("name"); String sal2 = rs.getString("sal"); System.out.println(empno2 + " " + empname2 + " " + sal2); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { //6、释放资源;从小到大关闭 if (rs != null) { try { rs.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } } JDBC编程中,常常会通过ResultSet rs来获得结果集,判断结果集是否为空往往不能直接判断rs == null 通常来说都是用rs.next()来判断结果集是否为空,但是由于执行rs.next()后指针指向的是结果集中的第一条记录,此时再用while(rs.next())取结果集中的数据就会导致第一条数据无法得到 因此用如下方法 if(!rs.next()) { //结果集为空,执行某操作 } else { //不为空,循环执行某操作 //ResultSet对象具有指向其当前数据行的指针,最初指针被置于第一行记录之前,通过next()方法可以 将指针移动到下一行记录 //将游标移到第一行前 rs.beforeFirst(); while(rs.next()){ } } 注:rs.next() 若不为空返回true;若是为空则返回false #### 二、SQL注入;Statement 和 PreparedStatement #### SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令 如下,用户在输入 用户名:admin ;密码:admin' or '1'='1 ;登录成功 select \* from login\_user where loginName = 'admin' and loginPwd = 'admin' or '1'='1' 这种现象被称为SQL注入(存在安全隐患) 2、SQL注入根本原因 用户输入的SQL语句中含有关键字,并且这些关键字参与了SQL语句的编译过程 导致SQL语句原意被修改,进行SQL注入 public static void main(String[] args) { //登录界面 Map<String,String> loginMap = initLoginUI(); //登录业务 boolean loginSuccess = login(loginMap); } // 含参数以及返回值注释快捷键 /**,然后Enter /** * 用户登录 * @param loginMap * @return */ private static boolean login(Map<String, String>loginMap) { boolean loginSuccess = false; //资源绑定器加载属性配置文件;不能带 .properties ResourceBundle bundle = ResourceBundle.getBundle("jdbc/jdbc"); String driver = bundle.getString("driver"); String url = bundle.getString("dburl"); String user = bundle.getString("user"); String pwd = bundle.getString("pwd"); String loginName = loginMap.get("loginName"); String loginPwd = loginMap.get("loginPwd"); Connection conn = null; Statement stmt = null; ResultSet rs = null; try { //1、注册驱动 Class.forName(driver); //2、获取连接 conn = DriverManager.getConnection(url,user,pwd); //3、获取数据库操作对象 stmt = conn.createStatement(); //4、执行SQL语句 String sql = "select * from login_user where loginName = '"+ loginName +"' and loginPwd = '" + loginPwd +"'"; rs = stmt.executeQuery(sql); //5、处理结果集 if (rs.next()){ loginSuccess = true; System.out.println("登录成功"); }else { System.out.println("账号密码错误"); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { //6、释放资源;从小到大关闭 if (rs != null) { try { rs.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } return loginSuccess; } /** * 用户登录UI * @return */ private static Map<String, String> initLoginUI() { Scanner sc = new Scanner(System.in); System.out.print("用户名:"); String loginName = sc.nextLine(); System.out.print("密码:"); String loginPwd = sc.nextLine(); Map<String,String> loginInfo = new HashMap<>(); loginInfo.put("loginName",loginName); loginInfo.put("loginPwd",loginPwd); return loginInfo; } 3、解决SQL注入问题 只要用户输入的信息不参与SQL语句的编译过程即可;即使用户输入的信息包含SQL语句关键字,没有参与编译就不会发生SQL注入 java.sql.PreparedStatement 预编译数据库操作对象 **java.sql.PreparedStatement**接口 继承了 **java.sql.Statement** java.sql.PreparedStatement原理:**预先对SQL语句进行编译,再给SQL语句传值** //SQL语句中 问号 ? 标识占位符,一个问号代表一个占位符;一个占位符传一个值;注:占位符不能使用单引号括起来 String sql = "select * from login_user where loginName = ? and loginPwd = ?"; //此处,发送SQL语句给DBMS,然后DBMS对SQL语句进行预编译 //预编译数据里操作对象 PreparedStatement = conn.prepareStatement(sql); //给占位符传值(第一个问号下标是1;JDBC所有下标从1开始) pstmt.setString(1,loginName); pstmt.setString(2,loginPwd); //4、执行SQL语句 rs = pstmt.executeQuery(); 既然 PreparedStatement 可以放置SQL注入现象,那直接 PreparedStatement 对象不是更好,为何还需要 Statement对象呢?存在即合理 ,因为有些业务场景必须使用Statement 4、Statement 和 PreparedStatement区别 (1)Statement 存在SQL注入问题;PreparedStatement 解决了SQL注入问题 (2)Statement 是编译一次执行一次;PreparedStatement 编译一次,执行N次,PreparedStatement效率更高 在数据库中,如果下一条SQL语句和上一条SQL语句完全一样(包括SQL大小写,空格位置等完全没变,如果增加了一个空格SQL都会重新编译),则下一条SQL语句执行不需要重新编译 (3)PreparedStatement 会在编译阶段做类型的安全检查 5、使用 Statement 场景 如下,我们在对名字排序时,发现使用PreparedStatement 设置参数报错,只能使用Statement 拼接 /**----- 使用PreparedStatement -----*/ /*String sql = "select * from emp order by empname ?"; //3、获取预编译数据库操作对象 pstmt = conn.prepareStatement(sql); pstmt.setString(1,"desc"); //4、执行SQL语句 rs = pstmt.executeQuery(); /* 报错 java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''desc'' at line 1 //5、处理查询结果集 while (rs.next()){ System.out.println(rs.getString("empname")); }*/ /**----- Statement -----*/ //3、获取数据库操作对象(专门执行sql语句的对象) stmt = conn.createStatement(); //4、执行SQL语句(DQL DML....) String key = "desc"; String sql = "select * from emp order by empname " + key; rs = stmt.executeQuery(sql); //5、处理查询结果集(只有当第四步执行的是select语句的时候,才有处理查询结果集) while (rs.next()){ System.out.println(rs.getString("empname")); } #### 三、JDBC事务 #### JDBC中 事务提交机制:自动提交 执行任意一条DML(insert delete update)语句,自动提交一次 如下,我们假设有一个银行账户111111,有余额20000万;111111向银行账户222222转账5000元,程序执行中间出了异常,导致111111账户上少了5000元,但是222222账户余额还是0;因此证明JDBC是自动提交 ![watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBATWluZ2dlUWluZ2NodW4_size_20_color_FFFFFF_t_70_g_se_x_16][] 关闭自动提交,conn.setAutoCommit(false); try { //1、注册驱动(连接的数据库) Class.forName(driver); //2、获取连接 conn = DriverManager.getConnection(url,user,pwd); // 将JDBC事务自动提交机制关闭 conn.setAutoCommit(false); String sql = "update bank_account set balance = ? where account_no =?"; //3、获取预编译数据库操作对象 pstmt = conn.prepareStatement(sql); pstmt.setDouble(1,15000.00); pstmt.setInt(2,111111); //4、执行SQL语句 int count = pstmt.executeUpdate(); //异常 String str = null; str.toString(); pstmt.setDouble(1,5000.00); pstmt.setInt(2,222222); int count1 = pstmt.executeUpdate(); //程序无问题提交事务 conn.commit(); } catch (ClassNotFoundException e) { //程序有异常,回滚事务 if (conn != null){ try { conn.rollback(); } catch (SQLException throwables) { throwables.printStackTrace(); } } e.printStackTrace(); } 如果程序无误,才会更新账户余额操作 ![watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBATWluZ2dlUWluZ2NodW4_size_20_color_FFFFFF_t_70_g_se_x_16 1][] #### 四、DBUtils工具类 #### package jdbc.utils; import java.sql.*; import java.util.ResourceBundle; public class DBUtil { //驱动类名 private static String driverClassName; //数据库URL private static String dbUrl; //数据库用户名 private static String userName; //数据库密码 private static String passWord; /** 工具类中的构造方法都是私有的 工具类中方法都是静态的,不需要new对象,直接采用类名调用 * */ private DBUtil(){} //静态代码快在类加载时执行,并且只执行一次 static { try { //资源绑定器加载属性配置文件;不能带 .properties ResourceBundle bundle = ResourceBundle.getBundle("jdbc/jdbc"); driverClassName = bundle.getString("driver"); dbUrl = bundle.getString("dburl"); userName = bundle.getString("user"); passWord = bundle.getString("pwd"); Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 获取数据库连接 * @return 返回连接 * @throws SQLException */ public static Connection getConnection() throws SQLException { return DriverManager.getConnection(dbUrl,userName,passWord); } // 含参数以及返回值注释快捷键 /** Enter /** * 关闭JDBC * @param rs 结果集 * @param stmt 数据库操作对象 * @param conn 连接 */ public static void close(ResultSet rs, Statement stmt,Connection conn){ //释放资源;从小到大关闭 if (rs != null) { try { rs.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } } #### 五、悲观锁和乐观锁 #### 1、悲观锁 和 乐观锁定义 悲观锁:事务必须排队执行;数据被锁住,不允许并发(行级锁:select后添加 for update) 乐观锁:支持并发,事务不需要排队,需要一个版本号 2、悲观锁 当要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观并发控制. 简单来说就是指某些数据被锁住了,事务需要这些数据的话,就必须排队获取,在当前事务结束之前,别的事务根本修改不了锁住的数据,不支持并发操作。 语句:SELECT ENAME ,JOB,SAL FROM EMP WHERE JOB='MANAGER' FOR UPDATE; 在select语句后面加了for update就产生了行级锁,所查询出的数据就会被锁住 在传统的关系型数据库多使用这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作数据之前先上锁。Java 里面的同步 synchronized 关键字的实现。 悲观锁主要分为共享锁和排他锁: (1)共享锁【shared locks】又称为读锁,简称S锁。顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。 (2)排他锁【exclusive locks】又称为写锁,简称X锁。顾名思义,排他锁就是不能与其他锁并存,如果一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务可以对数据行读取和修改。 3、乐观锁 乐观锁支持并发操作,事务不需要排队,只需要获取版本号,查看事务获取数据时和提交时的version号是否一致,一致就提交,不一致就不提交 当前类开启一个事务,这个事务仅做查询,并使用行级锁/悲观锁,锁住数据记录 //当前类开启一个事务,这个事务仅做查询,并使用行级锁/悲观锁,锁住数据记录 public class JDBCPessimisticLock { public static void main(String[] args) { Connection conn = null; PreparedStatement pStmt = null; ResultSet rs = null; try { conn = DBUtil.getConnection(); //开启事务 conn.setAutoCommit(false); //在select语句后面加了for update就产生了行级锁,所查询出的数据就会被锁住。 String sql = "select empname,job,sal from emp2 where job = ? for update"; pStmt = conn.prepareStatement(sql); pStmt.setString(1,"clerk"); rs = pStmt.executeQuery(); while (rs.next()){ System.out.println(rs.getString("empname") + "," + rs.getString("job") + "," + rs.getString("sal")); } //提交事务(事务结束);此处断点,没提交事务,另一个update事务无法执行返回结果 conn.commit(); } catch (SQLException throwables) { if (conn != null){ //回滚事务(事务结束) try { conn.rollback(); } catch (SQLException e) { e.printStackTrace(); } } throwables.printStackTrace(); } finally { DBUtil.close(rs,pStmt,conn); } } } 当前类负责修改被锁定的记录 //当前类负责修改被锁定的记录 public class JDBCPessimisticLock1 { public static void main(String[] args) { Connection conn = null; PreparedStatement pStmt = null; ResultSet rs = null; try { conn = DBUtil.getConnection(); //开启事务 conn.setAutoCommit(false); String sql = "update emp2 set sal = sal * 1.1 where job = ?"; pStmt = conn.prepareStatement(sql); pStmt.setString(1,"clerk"); int count = pStmt.executeUpdate(); System.out.println(count); //提交事务(事务结束) conn.commit(); } catch (SQLException throwables) { if (conn != null){ //回滚事务(事务结束) try { conn.rollback(); } catch (SQLException e) { e.printStackTrace(); } } throwables.printStackTrace(); } finally { DBUtil.close(rs,pStmt,conn); } } } [watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBATWluZ2dlUWluZ2NodW4_size_7_color_FFFFFF_t_70_g_se_x_16]: https://img-blog.csdnimg.cn/96167de218374bf48cdfd1d265784f85.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBATWluZ2dlUWluZ2NodW4=,size_7,color_FFFFFF,t_70,g_se,x_16 [watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBATWluZ2dlUWluZ2NodW4_size_10_color_FFFFFF_t_70_g_se_x_16]: https://img-blog.csdnimg.cn/c0d8416e0b924424bfdf4c6b67c4c0ad.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBATWluZ2dlUWluZ2NodW4=,size_10,color_FFFFFF,t_70,g_se,x_16 [watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBATWluZ2dlUWluZ2NodW4_size_7_color_FFFFFF_t_70_g_se_x_16 1]: https://img-blog.csdnimg.cn/f891f9e10c564947a283c4db4abca0a9.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBATWluZ2dlUWluZ2NodW4=,size_7,color_FFFFFF,t_70,g_se,x_16 [watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBATWluZ2dlUWluZ2NodW4_size_15_color_FFFFFF_t_70_g_se_x_16]: https://img-blog.csdnimg.cn/aba668e7714b4388b754f566cafd7b56.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBATWluZ2dlUWluZ2NodW4=,size_15,color_FFFFFF,t_70,g_se,x_16 [watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBATWluZ2dlUWluZ2NodW4_size_20_color_FFFFFF_t_70_g_se_x_16]: https://img-blog.csdnimg.cn/f76ddb8f7a2d4cec8dccb4fcd7c674cd.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBATWluZ2dlUWluZ2NodW4=,size_20,color_FFFFFF,t_70,g_se,x_16 [watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBATWluZ2dlUWluZ2NodW4_size_20_color_FFFFFF_t_70_g_se_x_16 1]: https://img-blog.csdnimg.cn/cca8afa36b9a46a49a160f4e11a16081.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBATWluZ2dlUWluZ2NodW4=,size_20,color_FFFFFF,t_70,g_se,x_16
相关 Java学习24:JavaJDBC下 ava学习24: JavaJDBC下: 链接:[https://pan.baidu.com/s/1IcilS0VNn9jcVNgjCeycRg][https_pan.... 桃扇骨/ 2024年04月17日 05:54/ 0 赞/ 76 阅读
相关 Java学习23:JavaJDBC上 ava学习23: JavaJDBC上: 链接:[https://pan.baidu.com/s/1cQzVvjzflbDy1qBml41FEQ][https_pan.... £神魔★判官ぃ/ 2024年04月17日 05:37/ 0 赞/ 89 阅读
相关 JavaJDBC:连接池 > 本篇内容包括:数据库连接池概述、JDBC 连接池原理、JDBC 连接池 Demo(addBatch demo、获取主键 demo、查看数据库的元数据 demo等)以及其他类 秒速五厘米/ 2023年09月24日 13:41/ 0 赞/ 119 阅读
相关 JavaJDBC:详解 > 本篇内容包括:JDBC 概述、JDBC 的执行流程(包括注册驱动、获取连接对象、创建 SQL 执行对象、执行SQL语句、遍历结果集、关闭资源(处理异常))以及 JDBC 的 喜欢ヅ旅行/ 2023年09月24日 13:36/ 0 赞/ 196 阅读
相关 java中事务代码大全,java JDBC使用事务示例,javajdbc事务示例,下面代码演示如何使用JD... java JDBC使用事务示例,javajdbc事务示例,下面代码演示如何使用JD 下面代码演示如何使用JDBC的事务。JDBC事务操作需要在执行操作之前调用Connecti ゞ 浴缸里的玫瑰/ 2023年01月13日 09:21/ 0 赞/ 147 阅读
相关 JavaJDBC编程中PreparedStatement的用法 PreparedStatement的用法 一个PreparedStatement是从java.sql.connection对象和所提供的sql字符串得到的,sql字符串中 快来打我*/ 2022年04月04日 06:52/ 0 赞/ 253 阅读
相关 基于javaJDBC技术的账务管理系统(思路+代码) 技术要点: 1.JDBC工具类的搭建 public class JDBCutils { private static BasicDataSource 喜欢ヅ旅行/ 2022年03月16日 06:36/ 0 赞/ 300 阅读
还没有评论,来说两句吧...