目录
前言
Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。
一句话说明:JDBC是Java操作数据库的技术。
一、连接数据库步骤【理解】
1、加载驱动类
2、创建数据库连接
3、创建SQL语句
4、创建PreparedStatement
5、替换占位符
6、执行SQL
7、如果是读操作(查询),处理结果集
8、关闭相关流
1、加载驱动类
private static String driver = "com.mysql.cj.jdbc.Driver";
Class.forName(driver);
com.mysql.cj.jdbc.Driver是MySQL的驱动类,利用反射的方式加载驱动类,是固定的写法。
2、创建数据库连接
private static String url = "jdbc:mysql://127.0.0.1:3308/dbname?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
private static String username = "root";
private static String password = "";
Connection connection = DriverManager.getConnection(url, username, password);
url是数据库连接地址,username是数据库用户名,password是数据库密码。
url说明:
数据库连接地址是固定写法,不同的数据库写法不同。
url还有简单写法,url = "jdbc:mysql://127.0.0.1:3308/dbname"。
127.0.0.1是数据库服务的ip地址。
3308是数据库端口号,注意:MySQL默认端口号是3306,我们安装的版本是解压版,修改了端口号为3308。
dbname是数据库名称,我们现在学习时统一取名叫dbname,减小学习难度。
问号后面的是添加数据库的其它参数,参数不写不会报错,但参数可以解决中文乱码、统一时区等问题。
3、创建SQL语句
String sql = "INSERT INTO student(name,age) VALUES (?,?)";
问号是占位符,后面会替换占位符。
4、创建PreparedStatement
PreparedStatement preparedStatement = connection.prepareStatement(sql);
PreparedStatement是Java JDBC API提供的一个接口,它用于编译和执行SQL语句,提供了一种更有效率和安全的方式来向SQL语句传递参数。
5、替换占位符
preparedStatement.setString(1, student.getName());
preparedStatement.setInt(2, student.getAge());
将第1个问号替换成student的name属性值,第2个替换成student的age属性值。
注意:在程序中的索引通常从0开始,此处是一个特例,索引从1开始。
除此之外还要知道PreparedStatement的父接口Statement,也可以编译和执行SQL语句,但是它不能做占位符的替换,需要在定义SQL时就把具体数据写到语句中:
String sql = "INSERT INTO student(name,age) VALUES (" + student.getName() + "," + student.getAge() + ")";
Statement statement = connection.getStatement(sql);
Statement是不推荐使用的,学习它的目的基本为了两个面试题:
PreparedStatement相较于Statement的优势是什么?
- 更高的执行效率。当需要执行多次相同的SQL语句时,PreparedStatement能够将SQL语句预编译,从而提高执行效率。PreparedStatement对象在创建时,会将SQL语句编译成一种可重用的二进制格式,当调用execute()方法时,直接传递参数即可执行SQL语句,而Statement当参数不同时也会认为是全新的SQL,会再次编译,Statement的每次都需要解析SQL语句导致了它的效率要低很多。
- 防止SQL注入攻击。使用Statement执行动态SQL语句时,需要将参数值拼接到SQL语句中,这样容易受到SQL注入攻击。而PreparedStatement通过参数化查询的方式,将参数值作为参数传递给SQL语句,从而避免了SQL注入攻击。
- 增加了代码的可读性。PreparedStatement的代码更加简洁明了,可以使代码更易于理解和维护。
什么是SQL注入?
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
例如:
登录功能,假定用户的账号和密码分别是123和abc。
登录时输入账号和密码分别为123和def,那么SQL为 select * from user where username=123 and password='def',执行SQL时查询不到数据,登录失败。
但如果用户输入的密码改为:(def' or '1'='1),那么SQL为 select * from user where username=123 and password='def' or '1'='1',即使密码输入的是错误的也可以查询到数据,就可以登录系统,这种情况就是SQL注入。
如果用PreparedStatement处理,生成的SQL为:select * from user where username=123 and password='def\' or \'1\'=\'1',输入的英文单引号会被转移,这样就查询不到数据,登录仍然失败。
当然,实际工作中用户登录的会应用更多的技术以提高安全性,现在只是举一个例子。
6、执行SQL
/*写操作*/
preparedStatement.execute();
/*读操作*/
ResultSet rs = preparedStatement.executeQuery();
7、如果是读操作(查询),处理结果集
List<Student> result = new ArrayList<>();
ResultSet rs = preparedStatement.executeQuery();
while(rs.next()){
Student s = new Student();
s.setAge(rs.getInt("age"));
s.setName(rs.getString("name"));
s.setId(rs.getInt("id"));
result.add(s);
}
ResultSet是查询的结果集,第一次执行next方法获取第一条数据,以后每次执行都会获取结果集中的下一条数据,当没有下一条数据时next方法会返回false。
8、关闭相关流
if(preparedStatement != null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
JDBC是流操作,所有流操作都要记得关闭流,否则资源会持续占用,关闭流的方式和学习IO流时方式相同,可以在finally中关闭,也可以通过try-with-resources的方式。
数据库连接Connection也是流,同样需要关闭,但不能提前关闭,否则会导致连接不到数据库。以后学习到框架后,各个流都交给框架管理,框架会统一处理流关闭问题。
二、CRUD完整代码【理解】
1、增加(Create)
public void add(Student student){
Connection connection = DBUtils.getConnection();
PreparedStatement preparedStatement = null;
String sql = "INSERT INTO student(name,age) VALUES (?,?)";
try(PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
/*替换占位符*/
preparedStatement.setString(1, student.getName());
preparedStatement.setInt(2, student.getAge());
preparedStatement.execute();
} catch (SQLException e) {
e.printStackTrace();
}
}
2、删除(Delete)
public void delete(int id){
Connection connection = DBUtils.getConnection();
PreparedStatement preparedStatement = null;
try {
String sql = "DELETE FROM student WHERE id=?";
preparedStatement = connection.prepareStatement(sql);
/*替换占位符*/
preparedStatement.setInt(1, id);
preparedStatement.execute();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(preparedStatement != null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3、修改(Update)
public void update(Student student){
Connection connection = DBUtils.getConnection();
PreparedStatement preparedStatement = null;
try {
String sql = "UPDATE student SET name=?, age=? WHERE id=?";
System.out.println(sql);
preparedStatement = connection.prepareStatement(sql);
/*替换占位符*/
preparedStatement.setString(1, student.getName());
preparedStatement.setInt(2, student.getAge());
preparedStatement.setInt(3, student.getId());
preparedStatement.execute();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(preparedStatement != null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
4、查询(Read)
public List<Student> select(Student student){
List<Student> result = new ArrayList<>();
Connection connection = DBUtils.getConnection();
PreparedStatement preparedStatement = null;
List<Object> params = new ArrayList<>();
try {
String sql = "SELECT * FROM student WHERE 1=1 ";
if(student.getId() != null){
sql += " AND id=? ";
params.add(student.getId());
}
if(student.getAge() != null){
sql += " AND age=? ";
params.add(student.getAge());
}
if(student.getName() != null){
sql += " AND name=? ";
params.add(student.getName());
}
preparedStatement = connection.prepareStatement(sql);
if(params != null && params.size() > 0){
for(int i = 0; i < params.size(); i++){
Object obj = params.get(i);
if(obj instanceof String){
preparedStatement.setString(i + 1, (String) obj);
}else if(obj instanceof Integer){
preparedStatement.setInt(i + 1, (Integer)obj);
}
}
}
ResultSet rs = preparedStatement.executeQuery();
while(rs.next()){
Student s = new Student();
s.setAge(rs.getInt("age"));
s.setName(rs.getString("name"));
s.setId(rs.getInt("id"));
result.add(s);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(preparedStatement != null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return result;
}
5、DBUtils
public class DBUtils{
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
private static final String URL = "jdbc:mysql://127.0.0.1:3308/dbname?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
private static final String USERNAME = "root";
private static final String PASSWORD = "";
private static Connection connection;
static {
Class.forName(DRIVER);
connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
}
public static Connection getConnection(){
return connection;
}
}