目录
通过Java语言操作数据库,操作表中的数据。
SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC。
是官方(sun公司)定义的一套操作所有关系型数据库的规则(接口)。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,运行时的代码其实是驱动jar包中的实现类。
在Java中要想访问数据库只能通过JDBC,JDBC是Java访问数据库的基础,其他数据库访问技术都是对JDBC的封装(Hibernate,MyBatis),JDBC是为了访问不同的数据库,提供了一种统一的访问方式,JDBC本身是Java连接数据库的一个标准,是进行数据库连接的抽象层。由Java编写的一组类和接口,接口的实现由各大数据库厂商来实现。
为了能够测试JDBC,这里利用junit进行测试,首先创建一个JavaWeb项目,在项目src包下创建一个测试类UserTest,并导入mysql-connector的jar包,这就是JDBC的核心jar包,没有它是不能调用其中的类方法的,jar包放到WEB-INF下的lib包内。
这里提供了mysql5版本和mysql8版本的jar包,对应的就是mysql的版本,如果是mysql5系列版本的就用5.16的jar包,如果是mysql8系列的版本就用8.0.16版本jar包。
链接:https://pan.baidu.com/s/1loxaN41BXlfdePT_8DDBDw
提取码:388i
添加到lib包下的jar包需要手动去导入一下右键jar包,点击ADD Library
创建一个t_user表,自行添加数据
- CREATE TABLE `t_user` (
- `id` int(11) NOT NULL,
- `username` varchar(20) DEFAULT NULL,
- `password` varchar(20) DEFAULT NULL,
- `age` int(11) DEFAULT NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
这里是我添加的数据
前期工作准备就绪后,就开始连接数据库,编写代码了,下面就演示一下CRUD操作
- public class UserTest {
- @Test
- public void select_User() throws ClassNotFoundException, SQLException {
- //加载mysql数据库JDBC驱动
- Class.forName("com.mysql.jdbc.Driver");
- //后面test是我t_user存放的数据库名
- String url="jdbc:mysql://localhost:3306/test";
- //数据库账户
- String username="xxx";
- //数据库密码
- String password="xxx";
- //获取connection对象
- Connection con = DriverManager.getConnection(url, username, password);
- //创建statement对象
- Statement statement = con.createStatement();
- //查询语句得到一个ResultSet类型的结果集
- ResultSet resultSet = statement.executeQuery("select * from t_user");
- //遍历结果集 输出控制台 next()方法如果有结果就返回true
- while (resultSet.next()){
- int id = resultSet.getInt("id");
- String username1 = resultSet.getString("username");
- String password1 = resultSet.getString("password");
- int age = resultSet.getInt("age");
- System.out.println(id+" "+username1+" "+password1+" "+age);
- }
- //后创建的先关闭
- resultSet.close();
- statement.close();
- con.close();
-
- }
- @Test
- public void add_User() throws ClassNotFoundException, SQLException {
- Class.forName("com.mysql.jdbc.Driver");
- String url="jdbc:mysql://localhost:3306/test";
- String username="xxx";
- String password="xxx";
- //获取connection对象
- Connection con = DriverManager.getConnection(url, username, password);
- //创建statement对象
- Statement statement = con.createStatement();
- //返回int 类型 代表的是修改的条数 executeUpdate可以为insert、delete、update操作
- int i = statement.executeUpdate("insert into t_user value(6,'王五','wda4822',26)");
- System.out.println(i);
- //后创建的先关闭
- statement.close();
- con.close();
- }
- @Test
- public void delete_User() throws ClassNotFoundException, SQLException {
- Class.forName("com.mysql.jdbc.Driver");
- String url="jdbc:mysql://localhost:3306/test";
- String username="xxx";
- String password="xxx";
- //获取connection对象
- Connection con = DriverManager.getConnection(url, username, password);
- //创建statement对象
- Statement statement = con.createStatement();
- int i = statement.executeUpdate("delete from t_user where id=6");
- System.out.println(i);
- //后创建的先关闭
- statement.close();
- con.close();
- }
- @Test
- public void update_User() throws ClassNotFoundException, SQLException {
- Class.forName("com.mysql.jdbc.Driver");
- String url="jdbc:mysql://localhost:3306/test";
- String username="xxx";
- String password="xxx";
- //获取connection对象
- Connection con = DriverManager.getConnection(url, username, password);
- //创建statement对象
- Statement statement = con.createStatement();
- int i = statement.executeUpdate("update t_user set username='宋浩',password='555sss4',age=24 where id=5 ");
- System.out.println(i);
- //后创建的先关闭
- statement.close();
- con.close();
- }
-
- }
在之前的代码中我们都是通过throws抛出异常,但是实际开发中需要我们去手动处理异常,下面我就以查询语句为例,演示如何处理异常
- public class UserTestException {
- @Test
- public void select_User() {
- try {
- Class.forName("com.mysql.jdbc.Driver");
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- //因为try里面为局部变量 所以需要在外面定义初始化,为后面释放资源提供方便
- Connection con = null;
- Statement statement = null;
- ResultSet resultSet = null;
- try {
- String url = "jdbc:mysql://localhost:3306/test";
- String username = "root";
- String password = "xxxx";
- //获取connection对象
- con = DriverManager.getConnection(url, username, password);
- //创建statement对象
- statement = con.createStatement();
- //查询语句得到一个ResultSet类型的结果集
- resultSet = statement.executeQuery("select * from t_user");
- //遍历结果集 输出控制台 next()方法如果有结果就返回true
- while (resultSet.next()) {
- int id = resultSet.getInt("id");
- String username1 = resultSet.getString("username");
- String password1 = resultSet.getString("password");
- int age = resultSet.getInt("age");
- System.out.println(id + " " + username1 + " " + password1 + " " + age);
- }
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- //后创建的先关闭
- try {
- if (resultSet != null) {
- //先判断是否为null 不为null就释放掉
- resultSet.close();
- //手动把资源赋值为null,提示gc垃圾回收机制释放资源
- resultSet = null;
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- try {
- if (statement != null) {
- statement.close();
- statement = null;
- }
-
- } catch (SQLException e) {
- e.printStackTrace();
- }
- try {
- if (con != null) {
- con.close();
- con = null;
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
-
- }
- }
以上代码虽然能够实现功能,但是代码的耦合度太高,比如加载驱动和定义url路径、用户、密码,以及最后的清除资源等,这都是重复工作,为了降低耦合度,我们需要把这些重复代码抽取出来到一个工具类中。
首先创建一个utils包,里面存放的就是工具类
- public class JDBCUtils {
- //利用静态代码块去加载驱动,随着类的加载而加载且加载一次
- static {
- try {
- Class.forName("com.mysql.jdbc.Driver");
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
- //获取连接对象
- public static Connection getConnection(){
- String url="jdbc:mysql://localhost:3306/test";
- String user="root";
- String password="xxxx";
- Connection con = null;
- try {
- con = DriverManager.getConnection(url, user, password);
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return con;
- }
- //释放查询语句资源
- public static void close(ResultSet rs,Statement stmt,Connection con){
- try {
- if (rs != null) {
- rs.close();
- rs = null;
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- try {
- if (stmt != null) {
- stmt.close();
- stmt = null;
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- try {
- if (con != null) {
- con.close();
- con = null;
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- //释放insert、delete、update语句的资源
- public static void close(Statement stmt,Connection con){
- try {
- if (stmt != null) {
- stmt.close();
- stmt = null;
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- try {
- if (con != null) {
- con.close();
- con = null;
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
我们再去测试功能时,代码就会得到简化,下面就是使用工具类以后
- @Test
- public void select_user(){
- Connection con = JDBCUtils.getConnection();
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = con.createStatement();
- rs = stmt.executeQuery("select * from t_user");
- while (rs.next()) {
- int id = rs.getInt("id");
- String username = rs.getString("username");
- String password = rs.getString("password");
- int age = rs.getInt("age");
- System.out.println(id + " " + username + " " + password + " " + age);
- }
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- JDBCUtils.close(rs,stmt,con);
- }
-
- }
目前还是有一点尚且优化的地方,就是我们填写jdbc的信息,以后肯定是会改变这些信息的,为了方便维护和查看,我们需要把这些信息抽取出来到一个配置文件进行存放,然后通过获取这个配置文件去加载内容。
在src目录下创建一个文件名为jdbc.properties
- jdbc.className=com.mysql.jdbc.Driver
- jdbc.url=jdbc:mysql://localhost:3306/test
- jdbc.user=root
- jdbc.password=xxxx
在工具类中进行优化
- public class JDBCUtils2 {
- public JDBCUtils2() {
- }
-
- private static String className;
- private static String url;
- private static String username;
- private static String password;
- private static Connection con = null;
-
- static {
- //初始化properties对象
- Properties pro = null;
- InputStream is=null;
- try {
- //通过反射获取配置文件io对象
- is = JDBCUtils2.class.getClassLoader().getResourceAsStream("jdbc.properties");
- pro = new Properties();
- //调用load方法读取jdbc.properties配置文件内容
- pro.load(is);
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- is.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- //分别调用getProperty方法获取对应的value值
- className = pro.getProperty("jdbc.className");
- url = pro.getProperty("jdbc.url");
- username = pro.getProperty("jdbc.user");
- password = pro.getProperty("jdbc.password");
-
- //加载驱动
- try {
- Class.forName(className);
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- //创建连接
- try {
- con = DriverManager.getConnection(url, username, password);
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
-
- public static Connection getConnection() {
- return con;
- }
-
- //释放查询语句资源
- public static void close(ResultSet rs, Statement stmt, Connection con) {
- try {
- if (rs != null) {
- rs.close();
- rs = null;
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- try {
- if (stmt != null) {
- stmt.close();
- stmt = null;
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- try {
- if (con != null) {
- con.close();
- con = null;
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
-
- //释放insert、delete、update语句的资源
- public static void close(Statement stmt, Connection con) {
- try {
- if (stmt != null) {
- stmt.close();
- stmt = null;
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- try {
- if (con != null) {
- con.close();
- con = null;
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
测试类代码
- @Test
- public void delete_user(){
- Connection con = JDBCUtils2.getConnection();
- Statement stmt = null;
- try {
- stmt = con.createStatement();
- int i = stmt.executeUpdate("delete from t_user where id=5");
- System.out.println(i);
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- JDBCUtils2.close(stmt,con);
- }
-
- }
首先创建一个bean包,用来存放实体类
- public class User {
- private String username;
- private String password;
- private int age;
-
- public User() {
- }
-
- public User(String username, String password, int age) {
- this.username = username;
- this.password = password;
- this.age = age;
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-
- @Override
- public String toString() {
- return "User{" +
- "username='" + username + '\'' +
- ", password='" + password + '\'' +
- ", age=" + age +
- '}';
- }
- }
测试类
- public class UserTest3 {
- @Test
- public void queryUser(){
- Connection conn = JDBCUtils2.getConnection();
- User user=null;
- Statement stmt=null;
- ResultSet rs=null;
- List
list=new ArrayList<>(); - try {
- stmt = conn.createStatement();
- rs = stmt.executeQuery("select * from t_user");
- while(rs.next()){
- user=new User();
- user.setUsername(rs.getString("username"));
- user.setPassword(rs.getString("password"));
- user.setAge(rs.getInt("age"));
- list.add(user);
- }
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- JDBCUtils2.close(rs,stmt,conn);
- }
- //遍历list集合
- list.forEach(System.out::println);
-
- }
- }
先来看这一问题
- public class UserTest3 {
- @Test
- public void queryUser(){
- Connection conn = JDBCUtils2.getConnection();
- User user=null;
- Statement stmt=null;
- ResultSet rs=null;
- List
list=new ArrayList<>(); - try {
- stmt = conn.createStatement();
- rs = stmt.executeQuery("select * from t_user where username=''or'1'='1'");
- while(rs.next()){
- user=new User();
- user.setUsername(rs.getString("username"));
- user.setPassword(rs.getString("password"));
- user.setAge(rs.getInt("age"));
- list.add(user);
- }
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- JDBCUtils2.close(rs,stmt,conn);
- }
- //遍历list集合
- list.forEach(System.out::println);
-
- }
- }
我在查询语句后面加了一个条件为username=''or'1'='1',我们现在运行程序
发现所有信息被查询出来了,这是怎么一回事呢,这就是SQL注入问题,是一个严重的安全隐患,那我们就要开始解决这个BUG,这也是字符串拼接带来的坏处,为了解决这个BUG,就要想办法不用字符串拼接,下面就讲述一下preparedStatment方法,其实现是通过占位符来完成的。
改造代码如下
- public class UserTest3 {
- @Test
- public void queryUser() {
- Connection conn = JDBCUtils2.getConnection();
- User user = null;
- // Statement stmt=null;
- PreparedStatement pstmt = null;
- ResultSet rs = null;
- List
list = new ArrayList<>(); - try {
- // stmt = conn.createStatement();
- // rs = stmt.executeQuery("select * from t_user where username=''or'1'='1'");
- //创建pstmt对象并赋值sql语句
- pstmt = conn.prepareStatement("select * from t_user where username= ?");
- //为占位符进行赋值,左边是占位符索引从1开始,右边为字符串内容
- pstmt.setString(1,"''or'1'='1'");
- rs = pstmt.executeQuery();
- while (rs.next()) {
- user = new User();
- user.setUsername(rs.getString("username"));
- user.setPassword(rs.getString("password"));
- user.setAge(rs.getInt("age"));
- list.add(user);
- }
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- JDBCUtils2.close(rs, pstmt, conn);
- }
- //遍历list集合
- list.forEach(System.out::println);
-
- }
- }
可见,这次再输入就查询不到了。