Java阶段二Day12
文章目录
- Java阶段二Day12
- JDBC
- 核心接口
- JDBC连接的基本流程
- 实现DDL
- 实现DML
- 实现DQL
- PreparedStatement
- V20
- DBUtil
- UserController
- 连接池
JDBC
JDBC Java数据库连接 Java Database Connectivity
,是java官方提供的一套结构,用于连接DBMS并进行相关操作
核心接口
- Connection: 表示数据库连接
- Statement: 用来执行SQL语句的语句对象
- PreparedStatement: 用来执行预编译SQL语句的语句对象用来表示查询结果集
- ResultSet: 用来表示查询结构集
JDBC连接的基本流程
- 加载驱动
- 通过DriverManager 与 DBMS 建立连接 获得 Connection对象
- 通过连接对象创建语句对象 Statement
- 通过语句对象 执行相应的 SQL语句给数据库
- 获得执行SQL的结果
实现DDL
public static void main(String[] args) {/*JDBC提供了连接,操作数据的流程,步骤大致如下:1:加载驱动2:通过DriverManager与DBMS建立连接(返回一个Connection)3:通过连接对象创建语句对象Statement4:通过语句对象执行相应的SQL语句给数据库5:获得执行SQL的结果*/try {//不同的数据库Driver的内容不同//1加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//mysql的驱动//2与数据库建立连接Connection connection = DriverManager.getConnection(//url格式是固定的 数据库地址 /数据库名?参数"jdbc:mysql://localhost:3306/tedu?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true","root","root");System.out.println("与数据库建立连接!");//通过连接对象创建用于执行SQL语句的语句对象Statement statement = connection.createStatement();//执行DDL语句String sql = "CREATE TABLE userinfo( " +" id INT PRIMARY KEY AUTO_INCREMENT, " +" username VARCHAR(30), " +" password VARCHAR(30), " +" nickname VARCHAR(30), " +" age INT(3)" +")";/*Statement提供了多种执行SQL的方法boolean execute(String sql)用于向数据库执行SQL语句。该方法可以执行任何种类的SQL但是实际上执行DML,DQL都有专门的方法,因此该execute通常只用来执行DDL该方法返回一个boolean值,执行该SQL后是否产生了查询结果集。*/statement.execute(sql);//执行该SQL语句connection.close();System.out.println("表创建成功");} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}
}
实现DML
public static void main(String[] args) {try {Class.forName("com.mysql.cj.jdbc.Driver");Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/tedu?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true","root","root");Statement statement = connection.createStatement();//向userinfo表中插入一条记录/*INSERT INTO userinfo (username,password,nickname,age)VALUES('张三','123456','阿三',22)*/// String sql = "INSERT INTO userinfo (username,password,nickname,age) " +// "VALUES('张三','123456','阿三',22)";Scanner scanner = new Scanner(System.in);System.out.println("欢迎注册");System.out.println("请输入用户名");String username = scanner.nextLine();System.out.println("请输入密码");String password = scanner.nextLine();System.out.println("请输入昵称");String nickname = scanner.nextLine();System.out.println("请输入年龄");int age = scanner.nextInt();String sql = "INSERT INTO userinfo (username,password,nickname,age) " +"VALUES('"+username+"','"+password+"','"+nickname+"',"+age+")";/*Statement提供的方法:int executeUpdate(String sql)专门用于执行DML语句的,返回值表达执行该DML后影响了表中多少条记录*/int num = statement.executeUpdate(sql);if(num>0){//至少执行后影响了表中1条记录System.out.println("插入成功");}else{System.out.println("插入失败");}connection.close();} catch (Exception e) {e.printStackTrace();}
}
public static void main(String[] args) {//修改某个用户的密码try {Class.forName("com.mysql.cj.jdbc.Driver");Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/tedu?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true","root","root");Statement statement = connection.createStatement();/*UPDATE userinfoSET password='666666'WHERE username='张三'*/String sql = "UPDATE userinfo " +"SET password='666666' " +"WHERE username='张三'";int num = statement.executeUpdate(sql);if(num>0){System.out.println("修改成功");}else{System.out.println("修改失败");}connection.close();} catch (Exception e) {e.printStackTrace();}}
实现DQL
DBUtil
/*** 用来管理数据库链接的类*/
public class DBUtil {static{try {Class.forName("com.mysql.cj.jdbc.Driver");//加载驱动} catch (ClassNotFoundException e) {e.printStackTrace();}}public static Connection getConnection() throws SQLException {return DriverManager.getConnection("jdbc:mysql://localhost:3306/tedu?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true","root","root");}
}
public static void main(String[] args) {try (Connection connection = DBUtil.getConnection();){Statement statement = connection.createStatement();/*查看所有老师的id,名字,工资,职称SELECT id,name,salary,titleFROM teacher*/String sql = "SELECT id,name,salary,title " +"FROM teacher ";ResultSet resultSet = statement.executeQuery(sql);/*ResultSet重要方法:boolean next()让结果集向下移动一行,返回值表示是否存在下一行。如果存在下一行则返回true否则返回false。*/while(resultSet.next()){//结果集向下移动一行,并判断改行是否存在//获取该条记录中id字段的值(id字段在数据库中是整数类型)int id = resultSet.getInt("id");//获取该条记录中老师的名字(name字段在数据库中是字符串类型)String name = resultSet.getString("name");//获取工资int salary = resultSet.getInt("salary");//获取职称String title = resultSet.getString("title");System.out.println(id+","+name+","+salary+","+title);}} catch (SQLException e) {e.printStackTrace();}
}
PreparedStatement
使用Statement
,拼接SQL语句会导致导致语义发生改变,即 SQL注入攻击,常见的如用户登录验证时在密码一栏填写:a' OR '1'='1
;
为防止SQL注入采用PreparedStatement
替代Statement
,实现预编译SQL
预编译SQL:可以先将SQL语句发送给数据库,让数据库生成执行计划理解该SQL的执行意图。然后SQL中需要的数据可以在需要执行该SQL时单独传给数据库。此时数据库仅会将传入的内容当作值看待,而不会因为拼接SQL导致语义发生改变,避免SQL注入攻击问题。
public static void main(String[] args) {/*预编译SQL语句的写法,将原本需要拼接的"值"先用"?"代替SELECT id,username,password,nickname,ageFROM userinfoWHERE username='拼接' AND password='拼接'预编译SQL:SELECT id,username,password,nickname,ageFROM userinfoWHERE username=? AND password=?*/String sql = "SELECT id,username,password,nickname,age " +"FROM userinfo " +"WHERE username=? AND password=?";try (Connection connection = DBUtil.getConnection();){//创建预编译SQL语句执行对象时要先将预编译SQL发送给数据库要求其理解语义PreparedStatement ps = connection.prepareStatement(sql);//执行该sql前要指明预编译SQL中"?"的值是什么/*?是什么类型的值,这里就应当调用对应的set方法例如?表示整数时,调用ps.setInt(...)?表示字符串时,调用ps.setString(...)方法第一个参数表示预编译SQL中第几个"?"."?"从左往右,从上往下,并且"?"从1开始,1表示第一个"?"。*/ps.setString(1,"张三");//第一个?值ps.setString(2,"123456");//第二个?值//将值传给数据库执行该SQLResultSet rs = ps.executeQuery();if(rs.next()){String nickname = rs.getString("nickname");System.out.println("登录成功,欢迎你:"+nickname);}else{System.out.println("登录失败,用户名或密码不正确");}} catch (SQLException throwables) {throwables.printStackTrace();}
}
V20
此版本成功和后台数据库连接
DBUtil
package com.birdboot.util;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class DBUtil {static {try {Class.forName("com.mysql.cj.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}}public static Connection getConnection() throws SQLException {return DriverManager.getConnection("jdbc:mysql://localhost:3307/birdbootdb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true","root", "root");}
}
UserController
package com.birdboot.controller;import com.birdboot.annotations.Controller;
import com.birdboot.annotations.RequestMapping;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;
import com.birdboot.util.DBUtil;
import entity.User;import java.io.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;/*** 处理与用户相关的业务*/
@Controller
public class UserController {private static File userDir;static {userDir = new File("./users");if (!userDir.exists()) {userDir.mkdirs();}}@RequestMapping("/regUser")public void reg(HttpServletRequest req, HttpServletResponse resp) {System.out.println("开始处理用户注册");String username = req.getParameter("username");String password = req.getParameter("password");String nickname = req.getParameter("nickname");String ageStr = req.getParameter("age");if (username == null || username.isEmpty() || password == null || password.isEmpty()|| nickname == null || nickname.isEmpty() || ageStr == null || ageStr.isEmpty()|| !ageStr.matches("[0-9]+")) {resp.sendRedirect("/reg_info_error.html");return;}int age = Integer.parseInt(ageStr);//查看是否为重复用户try (Connection conn = DBUtil.getConnection()){String sql = "SELECT 1 FROM userinfo WHERE username = ?";PreparedStatement ps1 = conn.prepareStatement(sql);ps1.setString(1,username);ResultSet rs = ps1.executeQuery();if (rs.next()){resp.sendRedirect("/have_user.html");}else {String sql2 = "INSERT INTO userinfo(username,password,nickname,age) " +"VALUES(?,?,?,?)";PreparedStatement ps2 = conn.prepareStatement(sql2);ps2.setString(1,username);ps2.setString(2,password);ps2.setString(3,nickname);ps2.setInt(4,age);int num = ps2.executeUpdate();if (num > 0){resp.sendRedirect("/reg_success.html");}}} catch (SQLException e) {e.printStackTrace();}}@RequestMapping("/loginUser")public void login(HttpServletRequest req, HttpServletResponse resp) {System.out.println("开始处理用户登录!!!");String username = req.getParameter("userName");String password = req.getParameter("password");if (username == null || username.isEmpty() || password == null || password.isEmpty()) {resp.sendRedirect("/login_info_error.html");return;}try (Connection conn = DBUtil.getConnection();){String sql = "SELECT username,password,nickname,age FROM userinfo " +"WHERE username = ? AND password = ?";PreparedStatement ps = conn.prepareStatement(sql);ps.setString(1, username);ps.setString(2,password);ResultSet rs = ps.executeQuery();if (rs.next()){//后期开发,将用户信息保存到 sessionresp.sendRedirect("login_success.html");}else {resp.sendRedirect("login_fail.html");}} catch (SQLException e) {e.printStackTrace();}}
}
连接池
与线程池同样道理。都是池化思想,重用内容和控制数量
学习使用阿里的DruidDataSource
maven设置:
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.21</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version>
</dependency>
DBUtil
package com.birdboot.util;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class DBUtil {//连接池private static DruidDataSource ds;static {initDruidDataSource();}private static void initDruidDataSource() {ds = new DruidDataSource();ds.setUsername("root");ds.setPassword("root");ds.setUrl("jdbc:mysql://localhost:3307/birdbootdb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true");//最大连接数ds.setMaxActive(30);//初始连接数ds.setInitialSize(5);}public static Connection getConnection() throws SQLException {return ds.getConnection();}
}