前言:
本篇博客将为大家介绍Java中的JDBC编程。
目录
简介
- 数据库编程的必备条件:编程语言,数据库和数据库驱动包(如:MySQL提供了Java的驱动包mysql-connector-java,需要基于Java来操作MySQL就需要这个驱动包。同理,要基于Java操作Oracle则需要Oracle的数据库驱动包ojdbc)。
- JDBC:Java Database Connectivity 数据库连接,是一种用于执行SQL语句的Java的API(Application Programming Interface应用程序编程接口)。它是Java中的数据库连接规范。这个包由java.sql.*,javax.sql.*包中一系列的类和接口组成,它为Java开发人员操作数据库提供了一个标准的API,可以为多种关系数据库提供统一访问。
- JDBC的优势有:1)Java语言访问数据库操作完全面向抽象接口编程。2)开发数据库应用不用限定在特定的厂商的API。3)程序的可移植性大大增强。
举例
步骤:
- 准备数据库驱动包,添加到项目的依赖中
- 建立数据库连接
- 创建操作命令
- 执行SQL语句
- 释放资源
1)准备数据库驱动包,添加到项目中:
- 先获取到一个驱动包
如何获取到一个驱动包呢:
Maven Repository: Search/Browse/Explore (mvnrepository.com)
进入网站以后在搜索栏搜索MySQL,然后选择适合自己MySQL的版本的驱动包(MySQL是什么版本,驱动包就选择什么版本。如MySQL是5版本,驱动包就选择5版本的;MySQL是8版本,驱动包就选择8版本的)
- 将驱动包放进一个项目中
在IDEA中创建lib目录:把驱动包引进该目录中:将lib目录标记成库:
3)建立数据库连接:
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
//1.创建一个DataSource,描述数据库所在源头DataSource
DataSource dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("自己数据库的密码");
//和数据库建立连接:
Connection connection = dataSource.getConnection();
注意:
- DataSource选择javax.sql包里面的(这才是JDBC的包)
- MysqlDataSource源自驱动包
- 在建立连接之前我们要先在MySQL中创建一张数据库,此处将其命名为Java
- 之所以第一步进行向上转型,后面又进行向下转型是为了让MysqlDataSource这个类名不要到处扩散,以便后续要把数据库修改成别的数据库改动不会太大。
- URL是计算机非常重要的概念,表示网络的资源位置,俗称网址。MySQL是一个客户端服务器的程序,是通过网络来进行交互的。同时,这里光给了网络只能找到MySQL服务器,还要对其进行认证(用户+密码)。root是管理员的意思。
- jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8&useSSL=false
- jdbc:mysql->描述url1的作用:给jdbc操作MySQL的
- 127.0.0.1->IP地址。用来描述网络上一个主机的地址。127.0.0.1是环回地址(loopback)。每台电脑上的环回地址都是这个,这里写环回地址就是自己把信息发给自己的意思。顺便一提,要想查看自己主机的IP地址只需要:win+r->cmd->ipconfig即可
- 3306:端口号,使地址更加精确,具体到哪个程序。
- java:访问MySQL服务器里的哪一个服务器。(就是我们前面创建的那个数据库的名字)
- utf8:utf8相当于MySQL里的增删改查那篇文章的utf8mb4。值得注意的是,这里不能写utf8mb4,utf8就相当于utf8mb4
- useSSL=false:是否要加密。此处我们选择的是未加密,因为在运行过程中,不同的计算机中环境不一样,加密可能会导致连接不上,且数据库中并无值得被攻击的东西,故选择不加密。
- ?:表示的是访问资源时需要哪些参数,多个参数之间的分隔符是&
- Connection是java.sql包下的
- getConnection可能会抛异常(执行sql或者操着数据库过程一般都会出现这个问题),解决该异常只需要throw 一下即可
3)创建操作命令:
//构造sql
String sql = "insert into student values(1,'张三')";
PreparedStatement statement = connection.prepareStatement(sql);
- 代码中的sql是不需要写分号的
- 此处举例举的是新增,查找,修改和删除的操作也是一样的,在构造sql的时候,只要把新增换成需要的操作即可。
- PreparedStatement statement = connection.prepareStatement(sql);是对sql语句进行预处理:解析检查sql语句,解析完毕会得到结构化数据(原来是字符串格式),在把这些格式化数据发送个数据库服务器。通过预处理,可以帮数据库省下解析的工作。
4)执行SQL语句:
//把sql发送个服务器,执行SQL
int n = statement.executeUpdate();
- 当sql语句是删除,修改和新增操作时,使用executeUpdate()方法(返回一个整数);是查看操作时,使用executeQuery()方法(返回一个结果集)
- n是一个整数,表示该操作会影响到的行数
5)释放资源:
//释放资源,关闭连接:先获取的后释放
statement.close();
connection.close();
- 程序通过代码和服务器进行通讯,会消耗一定的软/硬件资源,在程序结束的时候就需要告知服务器释放这些资源(客户端也需要释放这些资源)
完整代码:
public class Demo1 {
public static void main(String[] args) throws SQLException {
//1.创建一个DataSource,描述数据库所在源头DataSource
DataSource dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("自己的数据库密码");
//和数据库建立连接:
Connection connection = dataSource.getConnection();
//构造sql
String sql = "insert into student values(1,'张三')";
PreparedStatement statement = connection.prepareStatement(sql);
//把sql发送个服务器,执行SQL
int n = statement.executeUpdate();
//释放资源,关闭连接:先获取的后释放
statement.close();
connection.close();
}
}
运行后:
对于查询操作的完整代码:
public class Demo4 {
public static void main(String[] args) throws SQLException {
DataSource dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("自己数据库的密码");
Connection connection = dataSource.getConnection();
String sql = "select * from student";//Mysql中的各种select再这里都试用
PreparedStatement statement = connection.prepareStatement(sql);
//查询查询出来的是一个结果集,所以不能再用executeUpdate()方法了,它返回的是一个整数
ResultSet resultSet = statement.executeQuery();//resultSet表示的是一张临时表(结果集合)
//查询操作就是要看其返回出来的结果集,所以这里要加一个遍历结果集的操作
//通过next方法就可以获取到临时表中的每一行数据,如果获取到最后一行以后再往后走就会返回false
//next的起点并不是从第一行开始的,是从第一行的前一个位置,所以要先调用next,使之拿到第一行的数据
//类似于迭代器遍历,迭代器是hasNext(),判断是不是最后一个,不是,再来取元素,且迭代器一开始指向的是第一行的位置
while (resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
System.out.println("id = "+id+" , name ="+name);
}
resultSet.close();
statement.close();
connection.close();
}
}
是否可能让用户输入要修改的代码呢?
public class Demo2 {
public static void main(String[] args) throws SQLException {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入学号:");
int id = scanner.nextInt();
System.out.println("请输入姓名:");
String name = scanner.next();
DataSource dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("自己数据库的密码");
Connection connection = dataSource.getConnection();
String sql = "insert into student values(" +id+ ",'" +name+"')";
PreparedStatement statement = connection.prepareStatement(sql);
int n =statement.executeUpdate();
System.out.println(sql);
statement.close();
connection.close();
}
}
运行输入2 李四:
虽然这样确实是让用户自己输入了,但是这样不安全。在这种代码下,用户有很大的空间对数据库进行别的操作且这种写法不太优雅。下面这种写法可以很好地解决这个问题:
public class Demo3 {
public static void main(String[] args) throws SQLException {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入学号:");
int id = scanner.nextInt();
System.out.println("请输入姓名:");
String name = scanner.next();
DataSource dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("自己数据库的密码");
Connection connection = dataSource.getConnection();
String sql = "insert into student values(?,?)";//使用问号做为占位符
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1,id);//1表示替换第几个问号(从1开始算),id是int类型,所以用setInt;下面的name是String类型,所以用setString,是什么类型就用set什么
statement.setString(2,name);
int n = statement.executeUpdate();
statement.close();
connection.close();
}
}
运行输入10 王五:
其他:
- 内聚和偶合:两个代码间的联系联系紧密就是高耦合,反之就是低耦合;把相关联的代码放在一起就叫高内聚,反之就是低内聚。写代码应该追求高内聚低耦合。
- 在这里,数据库的连接方式有两种:
- 一种是通过DriverManager(驱动管理类)的静态方法:
// 加载JDBC驱动程序 Class.forName("com.mysql.jdbc.Driver"); // 创建数据库连接 Connection connection = DriverManager.getConnection(url);
- 一种是通过DataSource(数据源)对象获取。实际应用中会使用这个
DataSource ds = new MysqlDataSource(); ((MysqlDataSource) ds).setUrl("jdbc:mysql://localhost:3306/test"); ((MysqlDataSource) ds).setUser("root"); ((MysqlDataSource) ds).setPassword("root"); Connection connection = ds.getConnection();
- 这两种方法的区别有:
- DriverManger类来获取的Connection连接是无法重复利用的,每次使用完释放资源时,通过Connection.close()都是关闭物理连接
- DataSource提供连接池的支持。连接池在初始化时将创建一定数量的数据库连接,这些连接是可复用的,每次使用完数据库连接,释放资源调用Connection.close()都是将Connection连接对象回收。
- 一种是通过DriverManager(驱动管理类)的静态方法:
- Statement对象主要是将SQL语句发送到数据库中,Java API中主要提供了三种Statement对象:
- Statement用于执行不带参数的简单SQL语句
- PreparedStatement用于执行带或者不带参数的SQL语句;SQL语句会预编译在数据库中;执行速度快于Statement
- PreparedStatement归纳:
- 性能比Statement高
- 占位符?下标从1开始
- 可以阻止常见的SQL注入攻击
- SQL预编译,加快执行速度
- 占位符不能使用多值
- 参数化SQL查询
- PreparedStatement归纳:
- CallableStatement用于执行数据库存储过程中的调用