JDBC访问数据库操作详解(一)之JDBC基本用法:以MySQL为例

JDBC访问数据库操作详解(一)之JDBC基本用法:以MySQL为例

一、JDBC简介

1. JDBC是什么

​ Java Database Connectivity(JDBC)是Java程序访问数据库的方式

  • 提供了一套用于访问数据库的接口(API),其独立于特定的数据库(不区分特定数据库)

  • 不同的数据库产品,实现方式和通信协议都是不一样的

  • 由数据库厂商对接口进行具体实现,然后以jar包的形式提供实现类,这个jar包称为数据库驱动包

    注:jar包就是一个以.jar结尾的文件,其中包含了一些已经写好的类和接口,只需要将jar包导入到项目中,就可以直接使用jar包中的类和接口

2. 相关API

​ JDBC相关的类和接口都在java.sql包中

类/接口 作用
DriverManager 用于管理数据库驱动,建立与数据库的连接
Connection 表示与数据库的连接
Statement 用于执行SQL语句,并返回结果集ResultSet
PreparedStatement 用于执行预编译的SQL语句
ResultSet 表示结果集,封装了数据库返回的结果

二、JDBC基本用法

1. 下载jar包

​ 下载地址:https://dev.mysql.com/downloads/connector/j/

​ 注意要下载与自己操作系统以及mysql版本相对应的jar包版本

2. jar包的管理

​ 需要将jar包导入到项目中才能使用

导入:

  1. 在项目根目录下创建一个lib文件夹
  2. 将jar包拷贝到该文件夹中
  3. 右击jar包——>Build Path——>Add to Build Path

在这里插入图片描述

Referenced Libraries表示引用的库,jar包就被导入到该库中

移除:Referenced Libraries——>右击jar包——>Build Path——>Remove from Build Path

批量管理:右击项目——>Build Path——>Configure Build Path——>Libraries

3. JDBC访问数据库的操作步骤

1. 注册驱动
2. 获取连接
3. 获取Statement,用于执行sql语句
4. 执行sql语句
5. 处理结果
6. 关闭资源
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;


public class Test01 {

	public static void main(String[] args) {
		String driverClassName = "com.mysql.cj.jdbc.Driver";
		String url = "jdbc:mysql://localhost:3306/my_database?    
            useUnicode=true&characterEncoding=utf8&serverTimezone=GMT";
		String username = "root";
		String password = "aa54545677877";
        /**
         *提前声明Statement,Connection,不要在try里面声明,否则finally里关闭资源时访
         *问不到
         */
		Statement stmt = null;
		Connection conn = null;
		try {
			/**1.注册驱动
			 * Class.forName方法用于加载某个类,并返回这个类的对象,当然这里不需要接收
			 * 这个对象
			 * Class.forName方法需要捕获一个ClassNotFoundException异常
			 */
			Class.forName(driverClassName);
			/**2.获取连接
			 * url固定写法:"jdbc:mysql://服务器ip地址(如果是本地的话可以写
			 * localhost或是127.0.0.1):数据库连接端口
			 * (如果没有修改过,mysql默认的端口号是3306)/数据库名”
			 * username和password是自己数据库的用户名和密码
			 * DriverManager.getConnection需要捕获一个SQLException异常
			 */
		    conn = DriverManager.getConnection(url,username,password);
			//3.获取Statement,用于执行sql语句
		    stmt = conn.createStatement();
			/**4.执行sql语句
			 * 调用int executeUpadte(sql)方法,用于执行更新操作(增、删、改),返回
			 * 受影响的行数
			 */
			String sql = "insert into t_user(username,password,age) values 
                ('aaa','123',18)";
		    int num = stmt.executeUpdate(sql); 
		    //5.处理结果
		    System.out.println(num); //对于更新操作,结果一般无需处理
			} catch (SQLException e) {
				e.printStackTrace();
			}catch (ClassNotFoundException e) {
			e.printStackTrace();
		}finally {
			/**6.关闭资源
			 * 关闭的顺序要与打开的顺序相反:先关Statement,再关Connection
			 * close()方法也要捕获一个SQLException异常
			 */
			if(stmt != null) {
				try {
					stmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		

	}

}

4. 数据库中文乱码的解决

​ 细心的朋友也许会发现在上面的代码中url后面我加上了

?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT

​ 这句话会起到什么作用呢?

​ 上面的代码中第四步执行sql语句时,如果我们把sql 语句做一点小小的改变,例如:

`String sql = "insert into t_user(username,password,age) values 
            ('王二','123',18)";`

​ 把原来的’aaa’改成’王二’我们会发现表中的这项数据会出现乱码。

​ 当我们通过控制台插入中文数据是没有问题的,而当我们通过Java插入中文数据却可能会出现乱码,因为Java使用的编码跟我们的数据库编码可能不一致,所以在url中我们需要去指定编码

?useUnicode=true&characterEncoding=utf8

​ 当然这样做完你还是可能会出现报错

在这里插入图片描述

原因是你使用了最新版驱动,使用旧版驱动不会报错,可以在url后面再加上serverTimezone=GMT来解决,所以建议url的写法参照我上面的代码,当然关于这个报错还有其他解决方案,想要具体了解的话,请参考下面链接:https://blog.csdn.net/dreamboy_w/article/details/96505068

5. 数据库操作

5.1 更新

​ 使用int executeUpdate(sql)方法,用于执行更新操作(增、删、改),返回影响的行数

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class Test02_数据库操作 {
	public static void main(String[] args) {
		/*
		 *写增删改或建表、删表或建数据库、删数据库的sql语句作为参数传入update方法中即可
		 *实现更新操作 
		 *String sql = "insert into t_user(username,password,age) values 
		 *('aaa','123',18)";
		 *String sql = "delete from t_user where id>=5";
		 *String sql = "update t_user set password=666,age=18 where 
		 *username='tom'";
		 *String sql = "create table t_student(id int,name varchar(100))";
		 *String sql = "drop table t_student";
		 *String sql = "drop database my_database";
		 */
		update(sql);
		
	}
	
	/*
	 * 更新操作,适用于:
	 * insert/delete/update/create/drop
	 * 凡是在命令行中返回Query OK, 1 row affected(0.01 sec)这样形式的都适用
	 */
	public static void update(String sql) {
		Connection conn = null;
		Statement stmt = null;
		String driverClassName = "com.mysql.cj.jdbc.Driver";
		String url = "jdbc:mysql://localhost:3306/my_database?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT";
		String username = "root";
		String password = "aa54545677877";
		
		
		try {
			Class.forName(driverClassName);
			conn = DriverManager.getConnection(url, username, password);
			stmt = conn.createStatement();
			int num = stmt.executeUpdate(sql);
			System.out.println(num);//对于更新操作,结果一般无需处理
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			if(stmt != null) {
				try {
					stmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}

}

5.2 查询

​ 使用ResultSet executeQuery(String sql)方法,用于执行查询操作,返回查询到的结果集

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;



public class Test02_数据库操作 {
	public static void main(String[] args) {
		
		/*
		 * 实际开发中,严禁写select*
		 * 原因:1.效率低   2.可读性差  3.可能会导致错误
		 */
		String sql = "select id,username,password,age from t_user"; 
		List<User> users = query(sql);
		System.out.println(users);
	}
	
	/*
	 * 查询操作
	 */
	public static List<User> query(String sql) {
		List<User> list = new ArrayList<User>();
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		String driverClassName = "com.mysql.cj.jdbc.Driver";
		String url = "jdbc:mysql://localhost:3306/my_database?
            useUnicode=true&characterEncoding=utf8&serverTimezone=GMT";
		String username = "root";
		String password = "aa54545677877";
			
		try {
			Class.forName(driverClassName);
			conn = DriverManager.getConnection(url, username, password);
			stmt = conn.createStatement();
			rs = stmt.executeQuery(sql);//返回查询到的结果,以ResultSet形式返回
			/*
			 * ResultSet.next()方法:默认指向第一行上面,调用next()方法指针往下走一
			 *次,
			 *若有数据返回true,没有返回false 
			 *作用:1.指向下一条数据2.返回下一条数据是否存在  
			 *在一次循环中,不要多次调用next()方法,会跳过部分数据
			 */
			while(rs.next()) {  
			    	int id = rs.getInt(1);//根据列的编号来获取,从1开始
			    	String un = rs.getString(2);
			    	String pw = rs.getString("password");//根据列名来获取,实际中都采用这种方式
			    	int age = rs.getInt("age");
			    	User user = new User(id, un, pw, age);
			    	list.add(user);
			    }
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			if(rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(stmt != null) {
				try {
					stmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		
		return list;
		
	}

}

/*
 * 实体类
 */
class User {
	private Integer id;
	private String username;
	private String password;
	private Integer age;
	
	public User() {
		super();
	}

	public User(Integer id, String username, String password, Integer age) {
		super();
		this.id = id;
		this.username = username;
		this.password = password;
		this.age = age;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	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 Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "user [id=" + id + ", username=" + username + ", password=" + password + ", age=" + age + "]\n";
	}
}

一般返回结果都放在一个数据实体类型的集合中。

6. 封装JdbcUtil工具类

​ 通常习惯定义一个工具类将重复冗长的代码(比如数据库连接和关闭资源)封装

import java.sql.*;

/*
 * JDBC工具类,将重复的步骤封装
 */
public class JdbcUtil {
	/**
	 * 获取数据库连接
	 */
	public static Connection getConnection() {
		String driverClassName = "com.mysql.cj.jdbc.Driver";
		String url = "jdbc:mysql://localhost:3306/my_database?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT";
		String username = "root";
		String password = "aa54545677877";
		
		Connection conn = null;
		try {
			Class.forName(driverClassName);
			conn = DriverManager.getConnection(url,username,password);
		}catch (SQLException e) {
			e.printStackTrace();
		}catch (ClassNotFoundException e) {
		e.printStackTrace();
		}
		
		return conn;
	}
	
	/**
	 * 关闭资源
	 */
	public static void close(Connection conn,Statement stmt,ResultSet rs) {
		if(rs != null) {
	    	try {
	    		rs.close();
	    	}catch(SQLException e) {
	    		e.printStackTrace();
	    	}
	    }
		if(stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if(conn != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	public static void close(Connection conn,Statement stmt) {
		close(conn, stmt, null);
	}

}

7. PreparedStatement

​ PreparedStatement继承Statement,实际开发中推荐使用PreparedStatement

​ 优点:

  • 可以使用?占位符,简单易读(称为动态SQL)
  • 预编译SQL语句,效率高
  • 安全,避免出现SQL注入
import java.sql.*;
public class Test03 {
    public static void main(String[] args) {
    	/*
    	 * SQL注入:
    	 * 当使用Statement和对应的sql语句时,填有些特殊错误密码可以符合条件chaxun
    	 * 例如boolean flag = login("tom" ,"123' or '1=1");
    	 * 此时拼接的sql语句:
    	 * String sql = "select id,username,password,age from t_user where
    	 *         username='tom' and password=123' or '1=1'";
    	 * 此时where后面部分永远都是true,不管用户名和密码怎么写都能通过        
    	 */
    	boolean flag = login("tom" ,"666");
    	System.out.println(flag);
    	
    	
    	User user = new User(null, "ccc", "111", 25);
    	register(user);
    	System.out.println("注册成功");  	
    }
	
	/*
	 * 用户登录
	 */
	public static boolean login(String username,String password)
	{
		/*
		 * 正常sql语句写法:
		 * String sql = "select id,username,password,age from t_user where 
		 * username='" + username  + "' and password='" + password + "'";
		 * 这种拼接容易出错且可读性差
		 * 使用?占位符后写法:
		 * String sql = "select id,username,password,age from t_user where 
		 * username=? and password=?";
		 * 实际写的时候建议使用StringBuffer或StringBuilder拼接SQL语句,关键字、表
		 * 名、列名、条件等独占一行
		 */		
		String sql = new StringBuilder()
				.append(" select ")
				.append("  id,constant,date  ")
				.append(" from ")
				.append("  tb_test  ")
				.append(" where ")
				.append("  id = ? and constant = ?  ")
				.toString();
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;

		try {			
			conn = JdbcUtil.getConnection();
			ps = conn.prepareStatement(sql); //获取PreparedStatement,需要传入sql,进行预编译
			ps.setString(1, username);       //为占位符?赋值,编号从1开始,set后的类型只代表第二个参数类型,不代表数据库对应的类型,所以用setObject()也可以
			ps.setString(2, password);
			rs = ps.executeQuery();
			if(rs.next()) {  //如果返回结果集中最多只有一条记录,可以使用if
				return true;
			}
			
		}catch(SQLException e){
			e.printStackTrace();
		}finally {
			JdbcUtil.close(conn, ps, rs);
		}
		return false;
	}
	
	/**
	 * 用户注册
	 */
	public static void register(User user) {
		String sql = new StringBuilder() 
				.append(" insert into ")
				.append("  t_usert  ")
				.append("   (username,password,age)   ")
				.append(" values ")
				.append("   (?,?,?)   ")
				.toString();
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			conn = JdbcUtil.getConnection();
			ps = conn.prepareStatement(sql);
			ps.setString(1, user.getUsername());
			ps.setString(2, user.getPassword());
			ps.setInt(3, user.getAge());
			ps.executeUpdate();
			
		}catch(SQLException e){
			e.printStackTrace();
		}finally {
			JdbcUtil.close(conn, ps);
		}
	}
}

/*
 * 实体类
 */
class User {
	private Integer id;
	private String username;
	private String password;
	private Integer age;
	
	public User() {
		super();
	}

	public User(Integer id, String username, String password, Integer age) {
		super();
		this.id = id;
		this.username = username;
		this.password = password;
		this.age = age;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	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 Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public String toString() {
		return "user [id=" + id + ", username=" + username + ", password=" + password + ", age=" + age + "]\n";
	}
	
}

8. 实战练习:学生管理

​ 主要功能:

  1. 查询所有学生
  2. 根据学号查询学生
  3. 根据姓名和年龄范围查询学生
  4. 添加学生
  5. 修改学生
  6. 根据学号删除学生

​ 源码和SQL脚本文件也会上传,感兴趣的朋友可以下载。

​ 下载链接:https://download.csdn.net/download/zdfsb/12656254


本人将自己的学习过程详细记录,分为:

  • JDBC访问数据库操作详解(一)之JDBC基本用法:以MySQL为例
  • JDBC访问数据库操作详解(二)之JDBC更多用法:以MySQL为例
  • JDBC访问数据库操作详解(三)之数据库连接池:以MySQL为例
  • JDBC访问数据库操作详解(四)之三层架构:以MySQL为例
    对其他章节感兴趣的朋友可以关注我的博客浏览!感谢!

猜你喜欢

转载自blog.csdn.net/zdfsb/article/details/107553766