Java JDBC

一、介绍

  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+"\t"+uname+"\t"+upwd+"\t"+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

posted @ 2019-04-20 21:57  BalmyLee  阅读(239)  评论(0编辑  收藏  举报