JDBC(du)
JDBC
1、 jdbc是什么
java Database connectivity(java语句连接数据库)
2、JDBC 的本质是什么
JDBC是sun公司制定的一套接口(interface),
接口都有调用者和实现者。
面向接口调用,面向接口实现类。这都是属于面向接口的编程、
接口也是抽象编程
java.sql
为什么要面向接口编程?
解耦合:降低程序的耦合度,提高程序的扩展力。
多态机制是非常典型的的:面向抽象的编程。(不要面向具体编程)
建议 Animal a=new Cat(); Animal a=new Dog(); public void feed(Animal a){//面向父类形编程。 } 不建议 Dog d=new Dog(); cat c=new cat(); public void feed(Dog a){ }
为什么sun制定一套jdbc接口?
因为每一个数据库的底层实现原理都不一样。 ORacle 数据库有自己的原理。 MYSQL数据库也有自己的原理 ............. 数据库产品都有自己独特的实现原理。 数据库驱动:所有数据库都是以jar包形式的存在,jar 包中有很多.class文件 就是对jdbc接口的实现
3、jdbc开发
下载对应的驱动jar 包,然后将其配置到classpath环境变量里
4、重要类
重要的类
DriverManager :依据数据库的不同,管理JDBC驱动
Connection :负责连接数据库并担任传送数据的任务——通过DriverManager获取的
Statement :由 Connection 产生、负责执行SQL语句
ResultSet:负责保存Statement执行后所产生的查询结果
ResultSetMetaData:换取关于ResultDate对象中的类型和属性信息的对象
PreparedStatement:继承Statement接口
DriverManager
驱动程序管理器,跟踪可用的驱动程序,并在数据库和相应的驱动程序之间建立连接。
注册驱动程序:
Class.forName(“com.microsoft.sqlserver.jdbc. SQLServerDriver”);
Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”);
Class.forName( "oracle.jdbc.driver.OracleDriver ");
Class.forName(“com.mysql.jdbc.Driver”);
注册的驱动程序类名称必须在用户的classPath中。
//第一种注册加载驱动 //DriverManger.registerDriver(new Driver()); 放到静态代码块 try { Class.forName("com.mysql.cj.jdbc.Driver");//第二种 } catch (ClassNotFoundException e) { e.printStackTrace(); }
Connection
换取数据库连接 对象
Connection conn= DriverManager.getConnection(url,user,pwd); url:地址: jdbc : <subprotocol> : <subname> user:数据库名 pwd:密码
Statement
执行sql语句
由Connection接口的createStatement()方法创建。
编译一次执行一次
//获取Statement //executeupdate 增删改 executequery 查 Statement stm=conn.createStatement(); //4 执行SQL:executeupdate 就是执行增删改查 int i=stm.executeUpdate(sql);
ResultSet
处理查询结果集
在JDBC中数据库的所有查询记录将使用ResultSet接收并显示内容
ResultSet的常用方法:
next():判断有没有下一行,有指向下一行,并返回true,没有返回false
getObject(int index):索引,获取当前行第几列的值。1–Object
getObject(String lieming):列名,根据列名获取列名对应的值
getInt—int
getString-----
注意:随便的用来接注意类型
ResultSetMetaData
可以从这个对象获得有关数据库管理系统的各种信息,包括数据库中的各个表,表中的各个列,数据类型,触发器,存储过程等各方面的信息
ResultSetMetaData是在DatabaseMetaData类的对象上实现的,DataBaseMetaData对象又是在Connection对象上获得的。
getURL():返回一个String类对象,代表数据库的URL。
getUserName():返回连接当前数据库管理系统的用户名。
isReadOnly():返回一个boolean值,指示数据库是否只允许读操作。
getDatabaseProductName():返回数据库的产品名称。
getDatabaseProductVersion():返回数据库的版本号。
getDriverName():返回驱动驱动程序的名称。
getDriverVersion():返回驱动程序的版本号。
PreparedStatement
PreparedStatement 接口继承 Statement接口比普通的Statement对象更加灵活,有效解决SQL 注入问题(登录不要用statement)
区别
- Statement 可以先行创建, 然后将sql语句写入.执行时传入sql
- PreparedStatement 在创建时一定要传入 sql语句, 因为它要先运送到数据库执行预编译
- PreparedStatement 在执行之前 先要设置 语句中的参数. (预处理的sql语句有占位? 执行前需要给?指定参数值,执行时可以直接执行不需要传入sql)
- PreparedStatement 解决了sql注入问题
- PreparedStatement 效率高 (一个SQL语句一模一样的重复执行不会重新编译 用这个类的 话 执行不同的内容但是sql框架已经好了 编译一次可以执行n次)
- PreparedStatement 会在执行编译前阶段进行安全检查
总结::PreparedStatement 使用较多 ,只要少数情况的情况下需要使用Statement
只要在业务需要使用SQL注入的情况下必须使用Statement(需要代码拼接的·)
5、 jdbc编程六步(记)
- 注册驱动(作用:告诉java程序,即将连接的是哪一个品牌的数据库)
- 获取连接(表示jvm的进程和数据库进程之间的通道打开了,重量级的,使用完必须释放)
- 获取数据库操作对象(执行sql语句对象)
- 执行sql语句(DQL DML。。。)
- 处理查询结果集(只有当第四部执行的是select语句的时候,才有处理查询结果集)
- 释放资源(使用完一定要释放资源)
6 、 注入
当前程序存在的问题
用户名:随便
密码: ’ or ‘1’=’
登录成功
这种现象被称为sql注入。(黑客经常使用)
程序中有一个bug
导致的sql注入的根本原因是什么
用户输入的信息中含有SQL语句的关键字,那这些关键字参与啦sql语句的编译过程,
导致sql语句原意被扭曲,进而达到sql注入
使用PreparedStatement的预编译功能来解决sql的住入问题可以
7、事务
mysql的事务
事务:将多条sql当成整体去运行,要成功都成功,要失败都失败,事务的原子性(不可分割)
事务的特性:持久性(一经提交,数据会存储到数据库–存储磁盘中永久存在)
JDBC的事务
默认情况下连接COnnection属于自动模式 在自动提交模式下,每个SQL更新语句(insert,update,delete)成功执行完后就会自动提交到数据库中。
批处理
一次性需要新增上万条 上千数据,此时一条条处理,效率慢,数据库交互时间,可以进行批处理,可以将多条sql打成一个批次,推送给数据库执行一次。
conn.setAutoCommit(false);//关闭自动提交 默认为true
关闭来自动模式 每个sql都是事务的一部分
然后需要用commit()来进行显示的进行提交
在自动提交关闭后,不成功的提交会导致数据库进行隐式的回滚,所有的更新都会丢失。
conn.rollback();//回滚
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/kong?&serverTimezone=GMT%2B8&yseSSL=false","root","04161220"); conn.setAutoCommit(false);//关闭自动提交 String sql="insert into students_copy1(sno,sname,ssex) values(?,?,?)";//SQL语句 prstm=conn.prepareStatement(sql); for (int i=1;i<=100008;i++){ prstm.setObject(1,"aa"+1); prstm.setObject(2,"bb"); prstm.setObject(3,"bb"); //pstm.executeUpdate() 需要数据库交互100万次 prstm.addBatch();//添加 if(i%100==0){//没100条进行来打包 prstm.executeBatch();//执行一批 prstm.clearBatch(); } } prstm.executeBatch();// conn.commit();// try { conn.rollback();//回滚 } catch (SQLException ex) { ex.printStackTrace(); }
JDBC日期
日期赋值
1、给日期赋值 直接赋值日期格式的字符串
2、给日期赋值、java.util.Date dateUtil=new java.util.Date();//当前时间的年月日时分秒
java.sql.Date datesql =new java.sql.Date(dateUtil.getTime());//可以取出sql格式的年月日 当前的日期
3、给日期赋值 Jdk8中的LocakDate LocalDateTIme
LocalDateTime dateTime= LocalDateTime.now();//当前时间的年月日时分秒
LocalDate date=LocalDate.now();//当前时间的年月日时分秒
4、给日期赋值 java.sql.Timestamp dates=new java.sql.Timestamp(dateUtil.getTime());
5、 java.sql.Date datesql =new java.sql.Date(dateUtil.getTime());//可以取出sql格式的年月日 当前的日期
java.util.Date dateUtil=new java.util.Date();//当前时间的年月日时分秒 java.sql.Date datesql =new java.sql.Date(dateUtil.getTime());//可以取出sql格式的年月日 当前的日期 java.sql.Timestamp dates=new java.sql.Timestamp(dateUtil.getTime()); LocalDateTime dateTime= LocalDateTime.now();//当前时间的年月日时分秒 LocalDate date=LocalDate.now();//当前时间的年月日时分秒
JDBC查询日期
ResultSetMetaData resultSetMetaData= rs.getMetaData();//吧ResultSet 查询1的结果 给ResultSetMetaData 这样就有时间类型了 while (rs.next()){ // 注意在 高版本的jdk中 日期只有 LocalDate for (int i=1;i<resultSetMetaData.getColumnCount();i++){//循环他列数量 if(resultSetMetaData.getColumnClassName(i).equals("java.sql.Timestamp")){//判断这个例的值的属性是不是时间 java.sql.Timestamp timestamp=rs.getTimestamp(i);// 获取这个时间 java.util.Date date =new java.util.Date(timestamp.getTime()); //获取他 SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd"); System.out.println(format.format(date)); } } }
Dao模式
dao全称是data access object,数据库访问对象,主要的功能就是用于进行数据操作的,在程序的标准开发架构中属于数据层的操作。
什么是Dao模式
分离了业务逻辑代码和数据访问代码,分工明确,降低耦合性,提高可重用性。
采用面向接口编程,提高了项目的可扩展性和可维护性。
1、DAO接口: 把对数据库的所有操作定义成抽象方法,可以提供多种实现
2、DAO 实现类: 针对不同数据库给出DAO接口定义方法的具体实现。
3、实体类:用于存放与传输对象数据。没有不影响
4、数据库连接和关闭工具类: 避免了数据库连接和关闭代码的重复使用,方便修改。
配置文件
A. 后缀为.properties;
B. 格式是“键=值”格式;
C. 使用“#”来注释D. 让用户脱离程序本身修改相关的变量设置——使用配置文件
E. Java中提供了Properties类来读取配置文件F. 注意:第一:前面有 “ / ”:“ / ”代表了工程的根目录,例如工程名叫做myproject,“ / ”代表了myproject 。 eg:me.class.getResourceAsStream("/com/x/file/myfile.xml"); 第二:前面没有 “ / ”::代表当前类的目录。
代码示例
jdbc基础
public class Maintest01 { //1.加载驱动 //获取连接 public static void main(String[] args) { //第一种注册加载驱动 //DriverManger.registerDriver(new Driver()); 放到静态代码块 try { Class.forName("com.mysql.cj.jdbc.Driver");//第二种 } catch (ClassNotFoundException e) { e.printStackTrace(); } //2 获取连接 String user="root"; String pwd="04161220"; //协议 指明数据类型 连接谁的电脑 连接哪一个库 localhost String url="jdbc:mysql://localhost:3306/scool?&serverTimezone=GMT%2B8&useSSL=false"; Connection conn=null; Statement stm=null; try { conn= DriverManager.getConnection(url,user,pwd); //System.out.println(conn); //3 书写sql String sql=" insert into t_b values('a','b','c');"; //获取Statement //executeupdate 增删改 executequery 查 stm=conn.createStatement(); //4 执行SQL:executeupdate 就是执行增删改查 int i=stm.executeUpdate(sql); //查询的数据应该要excuteQuery System.out.println(i); } catch (SQLException e) { e.printStackTrace(); }finally{ //5 释放内存 //保证资源释放 //必须并列 一个不能关闭不能影响另一个的关闭 try { if(stm!=null){ stm.close();} } catch (SQLException e) { e.printStackTrace(); } try { if(conn!=null){ conn.close();} } catch (SQLException e) { e.printStackTrace(); } } } }
处理查询结果集
public static void main(String[] args) { Connection conn=null; Statement stmt=null; PreparedStatement stm=null; ResultSet rs=null; //1 try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } //是Statement对象的子接口,Statement可以进行它也可以——执行拼接字符串的sql fd //解决sql注入:?占位,避开字符串拼接的,需要动态写的内容不走拼接走?占位,在执行之前使用固定的方法给?赋值。 //2 &serverTimezone=GMT%2B8&useSSL=false try { //"jdbc:mysql://localhost:3306/scool?&serverTimezone=GMT%2B8&useSSL=false"; conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/scool?&serverTimezone=GMT%2B8&useSSL=false","root","04161220"); //3 stmt=conn.createStatement(); //4 insert into 表名(列名)values(列值),(),(),(),(),()。。。。 rs= stmt.executeQuery( "select * from T_b"); System.out.println(rs); while(rs.next()){ //数据列的位置 第一个位置的值第二个位置的值(用列的位置来换取这个是不安全的) String a=rs.getString(1); String b=rs.getString(2); String c=rs.getString(3); //不通过列的下标通过列的名字换取比较安全 //名称 String a=rs.getString("B1"); String b=rs.getString("B2"); String c=rs.getString("A1"); //除了可以以String 类型取出之外还可以,以特定的2类型取出 int a=rs.getInt(1); St System.out.println(a+","+b+","+c); } } catch (SQLException e) { e.printStackTrace(); }finally{ if(stmt!=null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
返回添加内容的主键的方法
/** * @author zhangyifan * @version 8.0 * @description: 获取新增的主键 * @date 2021/8/31 15:42 */ public class JDBC04 { static int age; public static void main(String[] args) { Connection con=null; PreparedStatement pstm=null; ResultSet rs=null; try {//1 Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace();//&serverTimezone=GMT%2B8&useSSL=false } //2 try {//"jdbc:mysql://localhost:3306/kong?&serverTimezone=GMT%2B8&yseSSL=false" con= DriverManager.getConnection("jdbc:mysql://localhost:3306/scool?&serverTimezone=GMT%2B8&yseSSL=false","root","04161220"); //3 String sql="insert into classinfo(classname,Begintime,endtime) values(?,?,?)";//sql语句 pstm= con.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);//返回新增的一个主键 pstm.setString(1,"aa"); pstm.setString(2,"2011-01-01"); pstm.setString(3,"2022-02-02"); //4 int i = pstm.executeUpdate();//新增的时候需要返回两个值 :1 影响条数 2 主键 rs= pstm.getGeneratedKeys();// pstm中有一个属性来存储主键 getGeneratedKeys(); 可以将主键值取出来 System.out.println(i); //5 if(rs.next()){ System.out.println(rs.getInt(1)); } } catch (SQLException e) { e.printStackTrace(); }finally { if (rs!=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (pstm!=null){ try { pstm.close(); } catch (SQLException e) { e.printStackTrace(); } } if (con!=null){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
封装的JDBC方法
工具类
/** * @author zhangyifan * @version 8.0 * @description: 03测试 * @date 2021/8/31 10:14 */ public class JdbcTest03 { public static void main(String[] args) { //1加载驱动 Jdbc03 baseDao=new Jdbc03(); //2/获取连接 Connection conn=null; PreparedStatement pstm=null; ResultSet rs=null; try { conn=baseDao.getConn(); //3写SQL语句 String sql="select * from students"; pstm=conn.prepareStatement(sql); //4执行sql语句 rs=pstm.executeQuery(); while (rs.next()){//5处理结果集 System.out.println(rs.getString(1)); } } catch (SQLException e) { e.printStackTrace(); }finally { baseDao.close(rs,pstm,conn); } //6释放资源 } } public class Jdbc03 { /* 工具类中的方法都是私有的 因为工具类中的方法都是是类名直接调用的 * */ private static String user; private static String pwd; private static String url; private static String classname; static{// 静态代码块在类加载时只执行一次 //1加载驱动 try { Properties m=new Properties(); InputStream is=Jdbc03.class.getResourceAsStream("db.properties"); m.load(is);//引进来 url=(String)m.get("db.url"); pwd=(String)m.get("db.pwd"); user=(String)m.get("db.user"); classname=(String) m.get("db.className"); Class.forName(classname); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public Connection getConn() throws SQLException { return DriverManager.getConnection(url,user,pwd); } public void close(ResultSet rs, Statement stm, Connection conn){ if(rs!=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stm!=null){ try { stm.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } properties文件 db.user=root db.pwd=04161220 db.url=jdbc:mysql://localhost:3306/kong?&serverTimezone=GMT%2B8&useSSL=false db.className=com.mysql.cj.jdbc.Driver
封装jdbc增删改查示例
package com.preson.JDBC04; import com.preson.Jdbc_03lx.JDBC01; import java.io.InputStream; import java.sql.*; import java.util.Properties; /** * @author zhangyifan * @version 8.0 * @description: 封装 * @date 2021/9/2 8:50 */ public class BaseDao { private static String url; private static String use; private static String pwd; private static String classname; static { Properties properties=new Properties();//获取对象 InputStream in=BaseDao.class.getResourceAsStream("db.properties");//获取连接 try { properties.load(in);//用properties对象获取里面的对象 //然后一个一个付值 url= (String) properties.get("db.4url"); use= (String) properties.get("db.4use"); pwd= (String) properties.get("db.4pwd"); classname= (String) properties.get("db.4classname"); Class.forName(classname);//获取连接 } catch (Exception e) { e.printStackTrace(); } } /** * * @return 连接数据库 * @throws SQLException */ public static Connection getconn() throws SQLException { return DriverManager.getConnection(url,use,pwd); } /** * *增删改查封装 * @param sql 语句 * @param objects 添加的对象 * @return 返回确认成功不 * @throws SQLException */ public int execupatd(String sql,Object[] objects) throws SQLException { Connection con = null; PreparedStatement prsm = null; try { con = this.getconn();//把添加中的两个需要改变的元素取出 sql语句 和 修改的内容 // String sql="insert into classinfo(classname,begintime,gradeid) values(?,?,?)"; prsm = con.prepareStatement(sql); //获取预编译sql语句 //修改的内容可以用数组来创建 吸收 // Object[] objects={"aaa","2021-01-02",3}; if (objects!=null&&objects.length>0) { for (int i = 0; i < objects.length; i++) {//用遍历数组的方式把?的值循环付上去 prsm.setObject(i + 1, objects[i]); } } int i=prsm.executeUpdate();//运行添加 返回的影响条数返回 return i; } catch (SQLException e) { e.printStackTrace(); throw e; } finally { this.close(con, prsm, null); } } /** * 查询新增主键 * @param sql * @param objects * @return 主键 * @throws SQLException */ public Object execupatdkey(String sql,Object[] objects) throws SQLException { Connection con=null; PreparedStatement prsm=null; ResultSet rs=null; try { con=this.getconn(); // String sql="insert into classinfo(classname,begintime,gradeid) values(?,?,?)"; prsm=con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); // Object[] objects={"aaa","2021-01-02",3}; for(int i=0;i<objects.length;i++){ prsm.setObject(i+1,objects[i]); } prsm.executeUpdate(); rs= prsm.getGeneratedKeys(); if(rs.next()){ return rs.getInt(1); } return -1; } catch (SQLException e) { e.printStackTrace(); throw e; }finally { this.close(con,prsm,null); } } /** * 查询封装 * @param sql * @param objects */ public void exeQuery(String sql,Object[] objects){ Connection con=null; PreparedStatement prtm=null; ResultSet rs=null; try { con=this.getconn(); } catch (SQLException e) { e.printStackTrace(); } try { prtm=con.prepareStatement(sql); if(objects!=null&&objects.length>0) {//进行判断 判断数组是否为空 如果为空证明没有用? for (int i = 0; i < objects.length; i++) { prtm.setObject(i + 1, objects[i]); } } rs = prtm.executeQuery(); ResultSetMetaData rsm = rs.getMetaData(); while (rs.next()) { for (int i = 1; i < rsm.getColumnCount(); i++) { System.out.print(rsm.getColumnName(i) + "----" + rs.getObject(i) + "\t"); } System.out.println(); } } catch (Exception e) { e.printStackTrace(); }finally { this.close(con,prtm,rs); } } //kong?&serverTimezone=GMT%2B8&yseSSL=false /** * 释放资源 * @param con * @param stm * @param rs */ public static void close(Connection con, Statement stm, ResultSet rs){ if(rs!=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stm!=null){ try { stm.close(); } catch (SQLException e) { e.printStackTrace(); } } if(con!=null){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南