JDBC(一)
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(); }