一、介绍
JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用java语言编写的类和接口组成。
有了JDBC,向各种关系数据发送SQL语句就是一件很容易的事。换言之,有了JDBC API,就不必为访问Sybase数据库专门写一个程序,为访问Oracle数据库又专门写一个程序,或为访问Informix数据库又编写另一个程序等等,程序员只需用JDBC API写一个程序就够了,它可向相应数据库发送SQL调用。
二、JDBC 的开发步骤
1)注册驱动:主要告诉 JVM 我们的程序将要使用哪一种数据库
2)获取连接:使用 JDBC 中的类,获得数据库的连接对象 Connection
3)获得语句执行平台:通过 Connection 可以获取执行者对象,Statement、PreparedStatement.
4)执行 SQL 语句:使用执行者对象,向数据库中执行 SQL 语句,然后可以得到对应的接口,有单个结果,也可能有结果集 ResultSet。
5)处理结果
6)释放对象:关闭顺序:rs -> stmt 、ptmt -> conn
演示代码:
1 public static void main(String[] args) {
2 // 提示用户输入用户名和密码
3 Scanner sc = new Scanner(System.in);
4
5 System.out.println("请输入用户名:");
6 String name = sc.nextLine();
7
8 System.out.println("请输入密码:");
9 String pwd = sc.nextLine();
10
11 Connection conn = null;
12 PreparedStatement pstmt = null;
13 ResultSet rs = null;
14 try {
15
16 // 1. 加载驱动
17 Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
18
19 // 2. 获取连接
20 conn = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;databaseName=testdb", "sa", "123456");
21
22 // 3. 利用conn,创建一个可以执行SQL语句的对象
23 String sql = "select * from userinfo where uname=? and upwd=?";
24 pstmt = conn.prepareStatement(sql);
25
26 // 执行Sql语句之前,先给“?”号赋值。
27 pstmt.setString(1, name);
28 pstmt.setString(2, pwd);
29
30 // 执行查询,不需要再给SQL语句
31 rs = pstmt.executeQuery();
32
33 if (rs.next()) {// 有了
34 System.out.println("登陆成功!");
35 } else {
36 System.out.println("登陆失败!");
37 }
38 while(rs.next()){//指针下移
39 int uid = rs.getInt("uid");
40 String uname = rs.getString("uname");
41 String upwd = rs.getString("upwd");
42 float umoney = rs.getFloat("umoney");
43
44 System.out.println(uid+" "+uname+" "+upwd+" "+umoney);
45 }
46
47 } catch (Exception e) {
48 e.printStackTrace();
49 } finally {
50 try {
51 rs.close();
52 pstmt.close();
53 conn.close();
54 } catch (Exception e2) {
55 e2.printStackTrace();
56 }
57 }
58 }
四、抽取 JDBC 工具类
在实际开发中,我们经常会碰到一些重复的代码,比如在获取某些资源的时候,解析某些数据的时候,都是需要重复去做一些相似的动作,这个时候很有必要抽取这些重复的代码成一个工具类,方面后面的使用。
很明显,我们每次操作数据库的,都需要连接数据库和关闭数据库,所有我们可以把数据库的连接和关闭都给抽取出来,作为公共部分。
新建一个 JDBCUtil.java 类。
1 public class JDBCUtils {
2 // 重构:一旦发现代码用起来不是很爽的时候,一直有重复动作,必须想把饭抽取出来一个新的
3 public JDBCUtils() {
4 }
5
6 public static Connection conn;
7
8 // 静态块:在类初始化之后,会加载一次
9 // 一般情况,我们会将一些常用的数据,放在静态块中进行加载
10 // 加载完成之后,后面如果想要用的话,直接就可以用
11 // 优点是,只会创建一次,不会重复创建良妃性能
12 static {
13 try {
14 // 1.注册驱动
15 Class.forName("com.mysql.jdbc.Driver");
16 // 2.获取连接:数据库地址、用户名、密码 Connection
17 String url = "jdbc:mysql://localhost:3306/test";
18 String user = "root";
19 String password = "root";
20 conn = DriverManager.getConnection(url, user, password);
21 } catch (Exception e) {
22 e.printStackTrace();
23 }
24 }
25
26 /**
27 * 获取 Connection 对象
28 */
29 public static Connection getConnection() {
30 return conn;
31 }
32
33 /**
34 * 释放资源
35 */
36 public static void close(Statement stmt, Connection conn) {
37 // 思路:在关闭资源之前,先要判断,到底有没有用到这个资源
38 if (stmt != null) {
39 try {
40 stmt.close();
41 } catch (SQLException e) {
42 e.printStackTrace();
43 }
44 }
45 if (conn != null) {
46 try {
47 conn.close();
48 } catch (SQLException e) {
49 e.printStackTrace();
50 }
51 }
52 }
53
54 public static void close(ResultSet rs, Statement stmt, Connection conn) {
55 if (rs != null) {
56 try {
57 rs.close();
58 } catch (SQLException e) {
59 e.printStackTrace();
60 }
61 }
62 if (stmt != null) {
63 try {
64 stmt.close();
65 } catch (SQLException e) {
66 e.printStackTrace();
67 }
68 }
69 if (conn != null) {
70 try {
71 conn.close();
72 } catch (SQLException e) {
73 e.printStackTrace();
74 }
75 }
76 }
77
78 }
改写上面的类:(可以发现抽出公共部分之后,以后每次写的代码都会大大减少)
@Test
public void fun2() throws Exception {
// 1.获取数据库连接
Connection conn = JDBCUtils.getConnection();
// 2.准备 SQL 语句
String sql = "select * from product";
// 3.获取执行者对象
Statement stmt = conn.createStatement();
// 4.执行 SQL 语句
ResultSet rs = stmt.executeQuery(sql);
// 5.处理结果
while (rs.next()) {
int idStr = rs.getInt("id");
String nameStr = rs.getString("name");
double priceStr = rs.getDouble("price");
String markStr = rs.getString("mark");
System.out.println(idStr + "--" + nameStr + "--" + markStr);
}
// 6.关闭资源
JDBCUtils.close(rs, stmt, conn);
}
五、SQL 的注入
SELECT * FROM product WHERE NAME = '伊利牛奶' AND price= 3.5
使用 PreparedStatement 对象,可以预防SQL 注入和提高 SQL 的预编译。
1 @Test
2 public void fun3() throws Exception {
3 // 1.获取数据库连接
4 Connection conn = JDBCUtils.getConnection();
5
6 // 2. 获取执行者对象
7 // 如果使用 Statement 的话,则有可能会被 SQL 注入破坏
8 // Statement stmt = conn.createStatement();
9 // 推荐使用 PreparedStatement,它是 Statement 的子类,
10 // 可以预防 SQL 注入,而且还可以把我们的 SQL 语句进行预初始化
11
12 // 3. 准备 SQL 语句
13 String sql = "select * from product where NAME = ? and price = ?";
14 PreparedStatement ptmt = conn.prepareStatement(sql);
15 // 这里的 1和2 表示的 where 后面的第n个属性
16 ptmt.setString(1, "伊利牛奶");
17 ptmt.setDouble(2, 3.5);
18
19 // 4. 执行 SQL 语句
20 ResultSet rs = ptmt.executeQuery();
21
22 // 5. 处理结果
23 while (rs.next()) {
24 int idStr = rs.getInt("id");
25 String nameStr = rs.getString("NAME");
26 String passStr = rs.getString("price");
27 System.out.println(idStr + "--" + nameStr + "--" + passStr);
28 }
29
30 // 6. 关闭资源
31 JDBCUtils.close(rs, ptmt, conn);
32 }
六、PreparedStatement 的简单使用
主要演示了“修改”功能,至于其他的功能,有时间再自己实践下。
1 @Test
2 public void fun4() throws Exception {
3
4 // 1.获取数据库连接
5 Connection conn = JDBCUtils.getConnection();
6
7 // 2. 准备 SQL 语句
8 String sql = "update product set name = ?, price = ? where id = ?";
9
10 PreparedStatement ptmt = conn.prepareStatement(sql);
11
12 ptmt.setString(1, "奥特曼");
13 ptmt.setString(2, "49.8");
14 ptmt.setInt(3, 1);
15
16 // 3. 执行 SQL 语句
17 // 此处的返回值是 int 类型,如果有一条数据发生改变的话,则返回 1
18 int i = ptmt.executeUpdate();
19
20 // 4. 处理结果
21 if (i == 1) {
22 System.out.println("恭喜你,修改成功了...");
23 }
24
25 // 6. 关闭资源
26 JDBCUtils.close(ptmt, conn);
27 }
七、封装数据
如果要封装数据的话,则需要新建一个对应的 JavaBean 类
如果要提供一个 JavaBean 的话,需要满足三个条件:、
1)实现序列化接口
2)提供字段
3)提供字段对应的 get 和 set 方法
注意: JavaBean 的命名,最好和对应的数据表一致。主要为了更好体现类与表的映射关系。
1 public class Product {
2 private int id;
3 private String NAME;
4 private double price;
5 private String mark;
6
7 public Product(int id, String nAME, double price, String mark) {
8 super();
9 this.id = id;
10 NAME = nAME;
11 this.price = price;
12 this.mark = mark;
13 }
14 public int getId() {
15 return id;
16 }
17 public void setId(int id) {
18 this.id = id;
19 }
20 public String getNAME() {
21 return NAME;
22 }
23 public void setNAME(String nAME) {
24 NAME = nAME;
25 }
26 public double getPrice() {
27 return price;
28 }
29 public void setPrice(double price) {
30 this.price = price;
31 }
32 public String getMark() {
33 return mark;
34 }
35 public void setMark(String mark) {
36 this.mark = mark;
37 }
38 }
查询所有数据:
1 /**
2 * 封装数据
3 * @throws Exception
4 */
5 @Test
6 public void fun5() throws Exception {
7
8 // 1. 获取数据库连接
9 Connection conn = JDBCUtils.getConnection();
10
11 // 2. 准备 SQL 语句
12 String sql = "select * from product";
13
14 PreparedStatement ptmt = conn.prepareStatement(sql);
15
16 // 3. 执行 SQL 语句
17 ResultSet rs = ptmt.executeQuery();
18
19 // 4. 创建一个集合,用来保存查询出来的对象实例
20 // 菱形语法,在泛型中,用来指定集合中存储的内容类型
21 List<Product> proList = new ArrayList<Product>();
22
23 // 5. 处理结果
24 while (rs.next()){
25 // 5.1 获取目标对象的实例,可以通过有参构造器
26 int id = rs.getInt("id");
27 String name = rs.getString("name");
28 double price = rs.getDouble("price");
29 String mark = rs.getString("mark");
30 Product pro = new Product(id, name, price, mark);
31 proList.add(pro);
32 }
33
34 // 5.2 查看保存数据的集合
35 for (Product pro : proList) {
36 System.out.println("商品的名字是:" + pro.getNAME());
37 }
38
39 // 6. 关闭资源
40 JDBCUtils.close(rs, ptmt, conn);
41 }
八、Properties 配置文件
在实际开发中,我们一般情况下,会把配置相关的信息,放在 xx.properties 中保存,并且使用。因为,以后代码有可能写的非常复杂,像一些不经常改动的东西,都建议抽取出来保存到配置文件中。当我们要用的时候,直接从配置文件中获取使用。当我们需要修改的时候,显示找到文件,然后就可以直接修改即可。很方便的。
一般情况下,都是写死的内容,不会经常改动的。
8.1 编写 jdbc.properties 文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
user=root
password=root
8.2 重写 JDBCUtils.java
1 public class JDBCUtils2 {
2 public JDBCUtils2() {
3 }
4
5 public static Connection conn;
6
7 private static String driver;
8 private static String url;
9 private static String user;
10 private static String password;
11
12 static {
13 try {
14 // 0.加载配置文件,获取对应的信息
15 readConfig();
16 // 1.注册驱动
17 Class.forName(driver);
18 // 2.获取连接:数据库地址、用户名、密码 Connection
19 conn = DriverManager.getConnection(url, user, password);
20 } catch (Exception e) {
21 e.printStackTrace();
22 }
23 }
24
25 /**
26 * 用来加载配置文件
27 *
28 * source folder 在部署之后,他的路径就是 bin 路径,也就是 classes 类资源路径
29 *
30 * @throws IOException
31 */
32 public static void readConfig() throws IOException {
33 // 1.找到文件,并且加载文件
34 InputStream is = JDBCUtils2.class.getClassLoader().getResourceAsStream("jdbc.properties");
35 System.out.println("is 的值:" + is);
36
37 // 2.从文件中获取数据
38 Properties pro = new Properties();
39
40 // 3.通过输入流加载数据
41 pro.load(is);
42
43 // 4.当获取到数据之后,应该把数据赋值给上面的变量
44 driver = pro.getProperty("driver");
45 url = pro.getProperty("url");
46 user = pro.getProperty("user");
47 password = pro.getProperty("password");
48 }
49
50 /**
51 * 获取 Connection 对象
52 */
53 public static Connection getConnection() {
54 return conn;
55 }
56
57 /**
58 * 释放资源
59 */
60 public static void close(Statement stmt, Connection conn) {
61 // 思路:在关闭资源之前,先要判断,到底有没有用到这个资源
62 if (stmt != null) {
63 try {
64 stmt.close();
65 } catch (SQLException e) {
66 e.printStackTrace();
67 }
68 }
69 if (conn != null) {
70 try {
71 conn.close();
72 } catch (SQLException e) {
73 e.printStackTrace();
74 }
75 }
76 }
77
78 public static void close(ResultSet rs, Statement stmt, Connection conn) {
79 if (rs != null) {
80 try {
81 rs.close();
82 } catch (SQLException e) {
83 e.printStackTrace();
84 }
85 }
86 if (stmt != null) {
87 try {
88 stmt.close();
89 } catch (SQLException e) {
90 e.printStackTrace();
91 }
92 }
93 if (conn != null) {
94 try {
95 conn.close();
96 } catch (SQLException e) {
97 e.printStackTrace();
98 }
99 }
100 }
101 }
九、整篇博客用到的 Java 代码打包地址:
https://github.com/yyzheng1729/JDBC