JDBC
JDBC(Java Data Base Connectivity)JAVA连接数据库技术的简称,是JAVA连接各种数据库的能力
连接的是关系对象型的数据库,Oracle SQLserver MYSQL
API Application Interface 应用程序接口
GUI Graphic User Interface 图形用户接口
JDBC工作原理:
SUN提供JDBC API集成在java.sql和javax.sql包中
JDBC API主要包括:
DriverManager类:启动管理器
Connection接口:连接建立与数据库之间的桥梁
Statement接口:处理器,执行负责将SQL语句发送到数据库
ResultSet接口:结果集,通过查询得到的虚拟表
JDBC作为Java应用程序与数据库连接的技术标准,本身并没有对JDBC API进行大量的实现,仅仅提供了数据库访问的抽象构建
因此JDBC只是对数据库的连接与数据处理访问提供了一套规范标准,这些规范标准主要以接口、抽象类的形式呈现。这些API的具体实现是由数据库厂商来实现完成的
JDBC驱动是不同数据库厂商
数据库厂商对JDBC API完成实现后的工程进行打包(.jar)后的文件,又称为数据库连接驱动包
JDBC的功能主要是实现如下处理:
将应用程序和数据库进行连接
执行SQL语句
将执行语句得到的结果返回应用程序
具体的分工:
DriverManager负责管理加载的驱动
Connection负责对数据库的连接
Statement由Connection产生,负责执行SQL语句
ResultSet保存Statement执行得到的结果(增删改返回的是受影响的行数)
JDBC中包含的核心对象:
Connection 连接接口对象,负责与数据库建立连接。由驱动管理器创建获得连接
DriverManager 驱动管理对象,负责加载数据库驱动,并完成连接的处理
用之前要先导入包:
File——ProjectStructure——Libraries——点击加号——导入“mysql-connector-java-5.1.42-bin.jar”包
DriverManager:
public class DriverManager{
//获取连接
public Connection getConnection(){
}
}
Connection:
public interface Connection{
//设置数据自动提交
public void setAutoCommit(boolean flag){
}
//提交数据,完成数据的持久化
public void commit(){
}
//关闭数据库的连接
public void close(){
}
//获取处理器
public Statement createStatement(){
}
}
Statement:
public interface Statement{
//执行持久化操作,返回受影响的行数
public int executeUpdate(String sql){
}
//执行查询,返回结果集
public ResultSet executeQuery(String sql){
}
public void close(){
}
}
JDBC进行数据库连接的方式主要有ODBC连接和纯JAVA驱动连接两种:
1.ODBC连接时需要配置当前系统的数据源,也是开发中使用的方式
2.JDBC不依赖当前系统环境,直接由驱动获取连接,仅用与操作系统提供的数据源进行访问连接
JDBC进行数据库操作访问的步骤主要如下:
1.加载驱动包
2.通过驱动管理获取应用程序和数据库的连接
3.通过连接获取处理器对象
4.使用处理器执行SQL语句
5.将执行得到的结果返回应用程序
6.关闭使用到的各个对象
URL 统一资源定位,用于在网络中查找定位到某一个精准位置的资源
URL的书写格式: 协议名称://IP地址:端口/资源路径
3306
http:// ftp:// file://
JAVA JDBC连接
Class.forName(“com.mysql.jdbc.Driver”);
Connection con=DirverManager.getConnection(“jdbc:mysql://ip地址:端口号:数据库名” , ”用户名” , ”密码”)
Access denied for user ......说明用户名或者密码出现错误
ClassNotFound......说明驱动路径或名称有错误
No suitable driver found for jbdc1:......说明URL协议路径有错误
Unknow Database......说明数据库名称写错了
//通过反射实现驱动的加载
Class.forName("com.mysql.jdbc.Driver");
//通过驱动管理器获得连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/geekhome", "root", "root");
System.out.println("数据库连接成功");
使用处理器执行SQL语句:
Statement stmt=con. createStatement(); //获得处理器
int count=stmt.executeUpdate(SQL语句); //使用处理器执行SQL语句,返回受影响行数
ResultSet rs=stmt.executeQuery(SQL语句); //使用处理器执行查询,返回结果集
关闭对象:
结果集.close();
处理器.close();
连接.close();
数据修改:
//通过连接对象获得处理器对象
Statement stmt = connection.createStatement();
//定义SQL语句
String sql = "insert into users values(null,'rose','1999-10-5','rose@geekhome.com',null,20,'女')";
String sql = "update users set age=21 where userid=104";
//使用处理器执行SQL语句,返回受影响的行数
int count = stmt.executeUpdate(sql);
System.out.println("受影响的行数:"+count);
//关闭对象
stmt.close();
connection.close();
课件练习:
将编号为102的员工的薪资降薪100
删除编号为197的员工信息
将工资最低的员工的薪资加薪10%
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class Demo1{
public static void main(String[] args) throws Exception{
//通过反射实现驱动的加载
Class.forName("com.mysql.jdbc.Driver");
//通过驱动管理器获得连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bbs_db", "root", "tjhilu");
System.out.println("数据库连接成功");
//通过连接对象获得处理器对象
Statement stmt = connection.createStatement();
//定义SQL语句
String sql1 = "update emp set salary=salary-100 where employee_id=100";
//使用处理器执行SQL语句,返回受影响的行数
int count1 = ((Statement) stmt).executeUpdate(sql1);
System.out.println("受影响的行数:"+count1);
String sql2= "delete from emp where employee_id=107";
int count2 = stmt.executeUpdate(sql2);
System.out.println("受影响的行数:"+count2);
String sql3="update emp set salary=salary*1.1 where employee_id in (select employee_id from (select employee_id from emp where salary=(select min(salary) from emp))n)";
int count3 = stmt.executeUpdate(sql3);
System.out.println("受影响的行数:"+count3);
//关闭对象
stmt.close();
connection.close();
}
}
查询数据:
//创建处理器
Statement stmt = connection.createStatement();
//定义SQL语句
String sql = "select * from dep";
//执行查询 获得结果集对象
ResultSet rs = stmt.executeQuery(sql);
//此处打印只会打印结果集对象地址
读取返回的结果:
使用结果集获取查询结果
while(rs.next()){
//根据每列的类型调用不同的方法
//如int型
rs.getInt(列所在索引);
//如Strig类型
rs.getString(列所在索引);
}
//迭代结果集
//next方法读取结果集中的下一行数据
while(rs.next()){
//每次循环表示读取到了一行
//读到行之后,需要根据列的字段类型调用相应的get方法,获取行中的每个列的值
//根据索引查找列值,索引从1开始
int depId = rs.getInt(1);
String depName = rs.getString(2);
int locationId = rs.getInt(4);
//获取列也可以根据列的名称访问
int managerId = rs.getInt("manager_id");
System.out.println(depId+"\t"+depName+"\t"+locationId+"\t"+managerId);
}
//关闭对象
rs.close();
stmt.close();
查询员工的姓名、薪水、入职时间、岗位(用日期函数接收数据库中的日期)
public static void main(String[] args) {
//查询员工的姓名、薪水、入职时间、岗位
try {
Class.forName("com.mysql.jdbc.Driver");
//获得连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/geekhome", "root", "root");
Statement stmt = connection.createStatement();
String sql = "select concat(first_name,last_name) as empName,salary,hire_date,job_id from emp";
//执行查询
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()){
//列索引取决于查询后的结果集(sql),而不是原表的列索引
//String empName = rs.getString(1);
String empName = rs.getString("empName");
double salary = rs.getDouble(2);
//rs.getDate方法返回java.sql.Date 该类型仅包含年月日
//Date hireDate = rs.getDate(3);
//获取包含完整时间的类型需要使用getTimeStamp方法,TimeStamp继承自Date
Date hireDate = rs.getTimestamp(3);
String job = rs.getString(4);
System.out.println(empName+"\t"+salary+"\t"+hireDate+"\t"+job);
}
//关闭对象
rs.close();
stmt.close();
connection.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
SQL语句中如果出现了变量将导致语句的拼接容易出错,更容易产生数据库注入的安全问题
使用PreparedStatement预处理解决上述问题
预处理器有以下优点:
1.提高数据处理的性能 (大幅度提高)
2.允许使用占位符注入参数,防止SQL注入的问题
处理器Statement和PreparedStatement的区别:
常规处理器Statement在每次执行SQL语句时都会先对SQL语句进行编译()
预处理器在创建时就要要求传入SQL语句,并在此时对SQL语句进行编译,将来每次调用时都不再执行编译的过程(因为将数据缓存在PreparedStatement中)
使用预处理器时,SQL语句出现的变量使用?进行占位
使用预处理器实现数据增删改:
要求键盘输入用户的姓名、生日(输入字符串,将其转换成日期类型)、邮箱、年龄
将输入的信息持久化至数据库中
public static void main(String[] args)throws Exception {
Scanner sc = new Scanner(System.in);
System.out.println("请输入姓名:");
String name = sc.nextLine();
System.out.println("请输入生日:(yyyy-MM-dd)");
String date = sc.nextLine();
System.out.println("请输入邮箱:");
String email = sc.nextLine();
System.out.println("请输入年龄:");
int age = sc.nextInt();
//将字符类型的生日转换成Date类型
Date birthday;
birthday = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(date);
Class.forName("com.mysql.jdbc.Driver");
//获得连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/geekhome", "root", "root");
//用拼接方法传入变量
// Statement stmt = connection.createStatement();
// //定义SQL语句
// String sql = "insert into users values(null,'"+name+"','"+date+"','"+email+"',null,"+age+",null)";
// System.out.println(sql);
//使用预处理器时,SQL语句出现的变量使用?进行占位(只有在adbc中才能使用)
String sql = "insert into users values(null,?,?,?,null,?,null)";
//通过预处理器解决SQL语句的注入问题
PreparedStatement pstmt = connection.prepareStatement(sql);
//将变量注入至占位符中
//调用的set方法取决于占位符写入的值的类型
//第一个参数表示占位符的索引,从1开始,第二个参数是要写入的变量(会自动加上引号)
pstmt.setString(1,name);
//mysql的时间类型可以使用字符类型进行隐式转换
//pstmt.setString(2, date);
pstmt.setTimestamp(2, new Timestamp(birthday.getTime()));
pstmt.setString(3, email);
pstmt.setInt(4, age);
//执行SQL语句
pstmt.executeUpdate();
pstmt.close();
connection.close();
}
使用预处理器实现模糊查询:
例子:根据员工姓名进行模糊查询,打印显示所有员工的姓名、薪水、所在部门的名称
public static void main(String[] args)throws Exception {
Scanner sc = new Scanner(System.in);
System.out.println("请输入要检索的姓名关键字");
String nameKey = sc.nextLine();
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/geekhome","root","root");
//创建SQL语句
String sql = "select concat(first_name,last_name) as empName,salary,department_name " +
"from emp inner join dep on emp.department_id=dep.department_id" +
" where concat(first_name,last_name) like ?";
//创建预处理器
PreparedStatement pstmt = con.prepareStatement(sql);
//注入参数
pstmt.setString(1, "%"+nameKey+"%");
//执行查询获得结果集
ResultSet rs = pstmt.executeQuery();
while(rs.next()){
String empName = rs.getString(1);
double salary = rs.getDouble(2);
String depName = rs.getString(3);
System.out.println(empName+"\t"+salary+"\t"+depName);
}
rs.close();
pstmt.close();
con.close();
}
获取结果集的元数据
ResultSetMetaData
该对象在执行后查询后,将会缓存结果集的数据结构
public static void main(String[] args) throws Exception{
String nameKey = "Steven";
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/geekhome","root","root");
//创建SQL语句
String sql = "select concat(first_name,last_name) as empName,salary,hire_date,department_name " +
"from emp inner join dep on emp.department_id=dep.department_id" +
" where concat(first_name,last_name) like ?";
//创建预处理器
PreparedStatement pstmt = con.prepareStatement(sql);
//注入参数
pstmt.setString(1, "%"+nameKey+"%");
//执行查询获得结果集
ResultSet rs = pstmt.executeQuery();
// while(rs.next()){
// String empName = rs.getString(1);
// double salary = rs.getDouble(2);
// String depName = rs.getString(3);
// System.out.println(empName+"\t"+salary+"\t"+depName);
// }
//通过结果集获取元数据
ResultSetMetaData metaData = rs.getMetaData();
//获取数据列的数量
int columnCount = metaData.getColumnCount();
System.out.println("列的数量:"+columnCount);
//根据列的数量循环遍历
for(int i = 1; i <= columnCount; i++ ){
//获取列的名称
String colName = metaData.getColumnName(i);
System.out.println(colName);
//获取列的数据类型
int type = metaData.getColumnType(i);
System.out.println(type);
//获取列的类型名称
String typeName = metaData.getColumnTypeName(i);
System.out.println(typeName);
}
rs.close();
pstmt.close();
con.close();
}