技术事简单报,看老王带你从入门JDBC,从删库到跑路
本篇文章适用于大学学习JDBC的入门同学,理解不深,欢迎交流
目录
1.JDBC是什么?
1.1JDBC的本质是什么?
1.2 JDBC的工具类存放位置
2. JDBC快速入门
3.JDBC各个类的讲解与理解
3.1 DriverManager 驱动管理对象
3.2 Connection 数据库 连接对象
3.3 Statement 执行SQL的对象
3.4 ResultSet 结果集对象
3.5 PreparedStatement 预执行SQL的对象
4.利用JDBC实现遍历数据库文件
5.制作JDBC工具类
6.回滚与提交问题
7.JDBC登录练习
8.乐观锁与悲观锁概念
总结
1.JDBC是什么?
Java Database Connectivity(java连接数据库)
1.1JDBC的本质是什么?
JDBC是sun公司制定的一套接口(interface),接口都有调用者和实现者,面向接口调用,面向接口写实现类,这都属于面向接口编程。位于
java.sql.* ;(这个软件包下有无数个接口)。
为什么要面向接口编程?
解耦合:降低程序的耦合度,提高程序的扩展力,多态机制就是非常典型的面对抽象编程(不要面对具体编程);
//多态
Animal a = new Cat();
Animal b = new Dog();
//父类找子类,父类型引用指向子类型对象
public void feed(Animal a){}
//面向抽象编程,可以完美实现开闭原则,可以让你的程序更加开阔,扩展更简单;
面向具体对象编程是程序员的大忌,要保持类多态的思维;
思考:为什么SUN要制定一套JDBC接口?
因为每一个数据库的底层实现原理都不一样,Oracle与MySQL的原理都不一样,为了统一实现所有功能
1.2 JDBC的工具类存放位置
java.sql包下的接口,即为JDBC的接口interface
具体的数据库实现类,需要在数据库的官网进行下载。
例如MySQL的实现类的形式(MySQL),为jar包存在,导入IDEA即可使用
2. JDBC快速入门
步骤
1:导入驱动jar包(数据库官网,例:mysql-connector-java),打开后将BINjar包导入即可
2:编写代码,注册驱动
3:获取数据库连接对象 Connection
4:定义SQL语句,发送给数据库,达到java代码操作数据库的要求
5:Connection无法直接执行SQL语句,所以 需要获取执行语句的对象 Statement
6:执行SQL语句,接受返回结果
7:处理结果,释放资源
//代码详解
public class jdbcD{
public static void main(String[] args) throws Exception{
//1.导入驱动jar包
//复制jar包到项目的libs目录下
//右键-->Add As Library
//2.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//3.获取数据库连接对象
Connertion conn= DriverManager.getConnection
("jdbc:mysql://localhost:3306/库名","数据库用户名","数据库密码");
//4.定义一个SQL语句
String SQL="select * from account"
//写一个需要动作的SQL语句
//5.获取执行SQL的对象Statement
Statement stmt = conn.createStatement();
//6.执行SQL
int count = stmt.executeUpdate(SQL);
//7.处理结果
System.out.println(count);
//8.释放资源
stmt.close();
conn.close();
}
}
3.JDBC各个类的讲解与理解
3.1 DriverManager 驱动管理对象
DriverManager 用于管理一组JDBC的基本服务
功能:1.注册驱动:告诉程序该使用哪一个数据库驱动jar
写代码使用: Class.forName("com.mysql.cj.jdbc.Driver");
通过查看源码发现,Driver类下,存在静态代码块:
注意:MySQL5之后的驱动jar包可以省略注册驱动的步骤,法律允许但不提倡,最好写。
2.获取数据库连接:
静态方法可以通过类名直接调用,有三个参数:
url:指定连接的路径,语法 jdbc:mysql://IP地址:端口号/数据库名称
例:("jdbc:mysql://localhost:3306/库名"");
小知识:如果连接的是本机的一个mysql服务器,并且mysql服务默认端口是3306
则url可以简写为 jdbc:mysql:///数据库名称?serverTimezone=Asia/Shanghai
(注意奥,老王当时在学习的时候用的MySQLjar包是8.0.26,这里没有写时区也能够使用,但是最好加上时区。法律允许但不提倡。最好写,别学老王,会成为混子的)
user:用户名
password:密码
3.2 Connection 数据库 连接对象
与特定数据库的连接会话
1.功能:
-
获取执行SQL对象的方法
Statement createStatement()
preparedStatement prepareStatement(String SQL)、
-
管理事务:
针对于开启事务,提交事务,回滚事务,分别做出管理:
开启事务:SetAutoCommit(boolean) :倒谱拍卖行该方法设置参数为false,即开启事务;
提交事务:commit(); 提交事务
回滚事务:rollback(); 回滚事务
3.3 Statement 执行SQL的对象
用于执行静态SQL语句并返回其生成的结果对象。
静态SQL:给定值的SQL参数
方法:
1.执行SQL:boolean execute(String SQL);执行给定的SQL语句(不常用)
2.int executeUpdate(String SQL); 执行DML语句(增删改语句)、DDL语句(创建,删除表),返回值int即为影响的行数(执行SQL语句后受到影响的语句),可以判断DML语句是否执行成功,返回值>0 即为执行成功,反之则失败
3.executQuery(String sql):执行DQL(select)语句,返回结果集对象
练习:
1.添加一条记录
2.修改一条记录
3.删除一条记录
3.4 ResultSet 结果集对象
executQuery(String sql):执行DQL(select)语句,返回结果集对象
查询后返回的列表即为结果集对象,ResultSet用来封装查询结果
游标一次获取一列内容
1.next():游标,向下移动一行,判断当前行是否是最后一行(是否有数据),如果是,则返回false,如果不是则返回true。
2.getxxx():获取数据,XXX代表获取的数据类型,例如int getInt(),getString()
括号内可以有参数,可以接受一个int值,代表列的编号,也可以接受一个String值,代表列的名称,如 gerDouble("xxxx")
注意.使用步骤:1.游标向下移动一行
2.判断是否有数据
3.获取数据(装载 打印,定义一个EMP集合,返回一个List
while(rs.next()){
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble(3);
System.out.println(id+" "+name+" "+balance);
}
3.5 PreparedStatement 预执行SQL的对象
Statement的子接口
4.利用JDBC实现遍历数据库文件
public class bianli {
public static void main(String[] args) {
List list = findAll();
for (emp e:list) {
System.out.println(e);
}
}
public static List findAll(){
ResultSet rs = null;
Statement stmt = null;
Connection conn = null;
List list = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/库名","数据库账号","数据库密码");
String sql ="select * from emp";
stmt = conn.createStatement();
rs=stmt.executeQuery(sql);
list = new ArrayList();
emp emp = null;
while (rs.next()) {
int EmpNo = rs.getInt("EMPNO");
String EName=rs.getString("ENAME");
String Job = rs.getString("Job");
int Mgr = rs.getInt("MGR");
Date HireDate = rs.getDate("Hiredate");
Double Sales = rs.getDouble("Sal");
Double Comm = rs.getDouble("COMM");
int DeptNo = rs.getInt("DEPTNO");
emp = new emp(EmpNo,EName,Job,Mgr,HireDate,Sales,Comm,DeptNo);
list.add(emp);
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally {
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 list;
}
}
============================================
//显示结果
emp{EmpNo=7369, EName='SMITH', Job='CLERK', Mgr=7902, HireDate=1980-12-17, Sal=800.0, Comm=0.0, Deptno=20}
emp{EmpNo=7499, EName='ALLEN', Job='SALESMAN', Mgr=7698, HireDate=1981-02-20, Sal=1600.0, Comm=300.0, Deptno=30}
emp{EmpNo=7521, EName='WARD', Job='SALESMAN', Mgr=7698, HireDate=1981-02-22, Sal=1250.0, Comm=500.0, Deptno=30}
emp{EmpNo=7566, EName='JONES', Job='MANAGER', Mgr=7839, HireDate=1981-04-02, Sal=2975.0, Comm=0.0, Deptno=20}
emp{EmpNo=7654, EName='MARTIN', Job='SALESMAN', Mgr=7698, HireDate=1981-09-28, Sal=1250.0, Comm=1400.0, Deptno=30}
emp{EmpNo=7698, EName='BLAKE', Job='MANAGER', Mgr=7839, HireDate=1981-05-01, Sal=2850.0, Comm=0.0, Deptno=30}
emp{EmpNo=7782, EName='CLARK', Job='MANAGER', Mgr=7839, HireDate=1981-06-09, Sal=2450.0, Comm=0.0, Deptno=10}
emp{EmpNo=7788, EName='SCOTT', Job='ANALYST', Mgr=7566, HireDate=1987-04-19, Sal=3000.0, Comm=0.0, Deptno=20}
emp{EmpNo=7839, EName='KING', Job='PRESIDENT', Mgr=0, HireDate=1981-11-17, Sal=5000.0, Comm=0.0, Deptno=10}
emp{EmpNo=7844, EName='TURNER', Job='SALESMAN', Mgr=7698, HireDate=1981-09-08, Sal=1500.0, Comm=0.0, Deptno=30}
emp{EmpNo=7876, EName='ADAMS', Job='CLERK', Mgr=7788, HireDate=1987-05-23, Sal=1100.0, Comm=0.0, Deptno=20}
emp{EmpNo=7900, EName='JAMES', Job='CLERK', Mgr=7698, HireDate=1981-12-03, Sal=950.0, Comm=0.0, Deptno=30}
emp{EmpNo=7902, EName='FORD', Job='ANALYST', Mgr=7566, HireDate=1981-12-03, Sal=3000.0, Comm=0.0, Deptno=20}
emp{EmpNo=7934, EName='MILLER', Job='CLERK', Mgr=7782, HireDate=1982-01-23, Sal=1300.0, Comm=0.0, Deptno=10}
进程已结束,退出代码为 0
5.制作JDBC工具类
目的:简化书写。
分析:1.注册驱动的抽取
2.抽取一个方法获取连接对象
3.抽取一个方法释放资源
封装!!封装!!封装!!
6.回滚与提交问题
通过事务管理实现回滚与提交
1.开启事务,conn.setAutoCommit(false);//取消自动提交机制
在事务结束的地方,只需要conn.commit()即可;
只要出现任何异常,都需要回滚数据。
if(conn != null){
conn.rollback();
}//当conn数据不等于空,就回滚数据,此方法一般写在异常
7.JDBC登录练习
详情见代码块
public class Jdbc07 {
public static void main(String[] args) {
//1.初始化界面
Map uesrLoginInfo = initUI();
//2.验证用户名密码;
boolean loinSuccexx = logina(uesrLoginInfo );
//3.输出结果
System.out.println(loinSuccexx ? "登录成功":"登陆失败");
}
//用户登录信息,false失败,true成功
private static boolean logina(Map uesrLoginInfo) {
//登陆标记
boolean bj = false;
//JDBC代码
Connection conn = null;
PreparedStatement stmt = null; //使用PreparedStatment 预编译的对象
ResultSet rs = null;
String loginName =uesrLoginInfo.get("loginName");
String LoginPWD = uesrLoginInfo.get("Password");
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306数据库名","账号","密码");
//3.获取预编译的数据库操作对象
String sql = "select * from t_user where loginName = ? and loginPwd = ?";//先写SQL框架 ?为占位符
stmt = conn.prepareStatement(sql);
//程序执行到此出,会发送SQL语句的框架给DBMS,然后DBMS进行语句的预先编译
//给占位符传值(第一个?下标是1 第二个?下表是2,JDBC中下标从1开始)
//由于是string SQL语句会自动加引号。
stmt.setString(1,loginName);
stmt.setString(2,LoginPWD);
//4.执行sql
rs = stmt.executeQuery();
//5.处理结果
if(rs.next()==true){
//登陆成功
System.out.println("欢迎您"+loginName);
bj = true;
}else{
System.out.println("您输入的账户或者密码有误");
}
} catch (Exception e) {
e.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 bj;
}
//初始化界面
private static Map initUI() {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名");
String LoginName= scanner.nextLine();
System.out.println("请输入密码");
String password = scanner.nextLine();
Map uesrLoginInfo = new HashMap();
uesrLoginInfo .put("loginName",LoginName);
uesrLoginInfo .put("Password",password);
return uesrLoginInfo ;
}
}
注 如果使用Statement 会存在SQL注入问题
SQL注入:即为web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。 此时使用预编译PreparedStatement 与占位符即可解决此BUG
8.乐观锁与悲观锁概念
当此语句后加 for update 即为行级锁
在这个SQL执行过程中,工作岗位在manager的信息被当前查询语句“锁住”,在当前事务没有结束的时候,其他的事务无法访问这三条信息。即为行级锁。又被称为悲观锁
总结
到这里就是老王在学习JDBC的全部笔记与操作记录了,有些笔记做在了代码里,太多实在无法整理,而且,真正在项目实战中已经很少用到JDBC了,但如果你是刚入门的学生,老王建议先用JDBC封装出一个通用的增删改查的方法,以便于后面学习JavaWeb打基础。希望老王的笔记能帮助大家度过考试的难关!撒花



