# JDBC

# 简介

JDBC(Java Database Connectivity)提供了一种与平台无关,执行SQL的标准API,可方便实现多种关系数据库的统一操作,它由一组Java语言编写的类和接口组成。这些标准需各个数据库厂商实现,每个数据库厂商都会提供一个JDBC驱动程序,常见的JDBC分为四类:

  • JDBC-ODBC bridge

    SUN提供JDBC操作标准,利用微软的ODBC进行连接操作,此模式会把所有的JDBC调用传递给ODBC,让其调用数据库的本地代码。

    • 优点是几乎所有数据库厂商提供对应的ODBC,可访问所有的数据库。

    • 缺点是执行效率低,不适合大数据量访问,需客户端预装ODBC驱动。

      odbc1

      odbc2

  • Native-API driver

    JDBC API调用转为原生C/C++ API本地调用。

    • 优点是速度快,比上面桥接模式快。

    • 缺点是客户端需要装数据库厂商的驱动程序,且这些驱动程序不是通用的。

      nativeAPI1

      nativeAPI2

  • Network-Protocol driver (Middleware driver)

    JDBC把对数据库的访问请求传递给中间件服务器,中间服务器再调用数据库服务器。

    • 优点是不需要再客户端加载数据库厂商的驱动程序,可扩展性好,便于做监控负载均衡。

    • 缺点是需要配置中间层,降低速度。

      NetworkProtocolDriver1

      NetworkProtocolDriver2

  • Database-Protocol driver (Pure Java driver) or thin driver

    纯基于Java的驱动程序,通过Socket与数据库厂商数据库进行通信。

    • 优点是平台独立,不需要中间媒介,应用能同数据库服直接通信,访问速度快。

    • 缺点是几乎只有数据库厂商自己才提供这种驱动,需针对不同的数据库使用不同的驱动。

      DatabaseProtocolDriver1

      DatabaseProtocolDriver2

JDBC的类结构如下:

JDBCClass

这些类同数据库的关系如下:

JDBCClassDatabase

# 选择策略

  • 如果访问的是Oracle,且并不需要分布式架构时,可考虑Database-Protocol driver。
  • 如果Java需同时访问多个数据库,或者需要分布式架构时需使用Network-Protocol driver。
  • 在没有上面的驱动时使用Native-API driver。
  • 在学习环境中或者开发测试环境中可使用JDBC-ODBC桥接。

# 数据库操作

# 连接数据库

JDBC属于Java提供的服务标准,它的操作步骤如下:

  • 加载数据库驱动程序。一般为jar或zip包,需配置到CLASSPATH路径。
  • 连接数据库。
  • 操作数据库。
  • 关闭数据库连接。

我们使用maria作为数据库,需要先下载jdbc驱动程序:JDBC-maria (opens new window)

在Eclipse中找到项目->属性->JavaBuildPath->Libraries,添加下载的JDBC驱动。

下面是连接程序:

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

class jdbcOper {
    String driverName = "org.mariadb.jdbc.Driver";
    String url = "jdbc:mysql://localhost:6612/test";
    String username = "root";
    String password = "666666";
    Connection connector = null;

    public jdbcOper() {
        try {
            Class.forName(driverName);
            // DriverManager.registerDriver(new Driver());
            connector = DriverManager.getConnection(url, username, password);
            System.out.println(connector);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    protected void finalize() throws Throwable {
        // TODO Auto-generated method stub
        super.finalize();
        connector.close();
    }

}

public class Main {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        jdbcOper jdbc = new jdbcOper();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# Statement

Statement是JDBC中操作数据库的接口,可实现数据的更新与查询的处理操作。注意数据查询时向数据发出select指令,查询的结果必须通过ResultSet接口来封装,ResultSet是一种可以保存任意查询结果的集合结构,所有查询结果通过ResultSet在内存中形成一张虚拟表,可根据数据行的索引,从每行依据数据类型来获取列数据。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;


class jdbcOper {
    String driverName = "org.mariadb.jdbc.Driver";
    String url = "jdbc:mysql://localhost:6612";
    String username = "root";
    String password = "666666";
    String defaultDatabase="test";
    Connection connector = null;
    Statement statement;

    public jdbcOper() {
        try {
            Class.forName(driverName);
            // DriverManager.registerDriver(new Driver());
            connector = DriverManager.getConnection(url, username, password);
            System.out.println(connector);
            statement = connector.createStatement();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void CreateDatabaseAndTable() {
        try {
            statement.execute("create database test;");
            UseDateBase();
            // statement.execute("create table student(learn_id int not null
            // auto_increment,name varchar(20) not null,score int,primary key(learn_id));");
            String createTableString = new StringBuilder()
                    .append(" create table student( ")
                    .append(" learn_id int not null auto_increment, ")
                    .append(" name varchar(20) not null, ")
                    .append(" score int, ")
                    .append(" date datetime, ")
                    .append(" primary key(learn_id)); ").toString();
            statement.execute(createTableString);
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println("创建数据库出错!");
        }
    }
    
    public void UseDateBase() {
        try {
            statement.execute("use "+defaultDatabase+";");
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void InsertStudentData(String name, float score, String dateString) {
        UseDateBase();
        String insertDataString = new StringBuilder()
                .append(" insert into student ")
                .append(" (name,score,date) ")
                .append(" values( ")
                .append(GetQuotedString(name)+ "," + GetQuotedString(score) + "," + GetQuotedString(getTime(dateString)))
                .append(" )")
                .toString();
        System.out.println("insert string:"+insertDataString);
        try {
            statement.execute(insertDataString);
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    public int updateStudentData() {
        String updateString = new StringBuilder()
                .append(" update student set ")
                .append(" score = "+GetQuotedString(90))
                .append(" where name="+GetQuotedString("xie"))
                .toString();
        int count=0;
        try {
            System.out.println("update string:"+updateString);
            count = statement.executeUpdate(updateString);
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("更改了数据:"+count+"行");
        return count;
    }
    
    public int deleteStudentData() {
        String deleteString = new StringBuilder()
                .append(" delete from student ")
                .append(" where learn_id in (2,3); ")
                .toString();
        int count = 0;
        try {
            System.out.println("delete string:"+deleteString);
            count = statement.executeUpdate(deleteString);
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("删除了数据:"+count+"行");
        return count;
    }
    
    public void SelectData() {
        UseDateBase();
        String selectString = new StringBuilder()
                .append(" select name,score,date ")
                .append(" from student ")
                .toString();
        try {
            ResultSet rs = statement.executeQuery(selectString);
            while(rs.next()) {
                System.out.println("name:"+rs.getNString(1)+" score:"+rs.getInt(2)+" date:"+rs.getDate(3)+" time:"+rs.getTime(3));
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    public String GetQuotedString(Object field) {
        return "\""+field.toString()+"\"";
    }

    public Timestamp getTime(String dateString) {
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss", Locale.ENGLISH);
        dateFormat.setLenient(false);
        Date timeDate = null;
        try {
            if ("current".equalsIgnoreCase(dateString)) {
                timeDate = new Date();
            } else {
                timeDate = dateFormat.parse(dateString);
            }
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return new Timestamp(timeDate.getTime());
    }

    @Override
    protected void finalize() throws Throwable {
        // TODO Auto-generated method stub
        super.finalize();
        connector.close();
    }

}

public class Main {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        jdbcOper jdbc = new jdbcOper();
        jdbc.CreateDatabaseAndTable();
        jdbc.InsertStudentData("xie", 70, "current");
        jdbc.InsertStudentData("wang", 80, "current");
        jdbc.InsertStudentData("zhang", 90, "current");
        jdbc.InsertStudentData("li", 30, "2018-07-27 12:25:00");
        jdbc.updateStudentData();
        jdbc.deleteStudentData();
        jdbc.SelectData();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182

# PreparedStatement

PreparedStatement是Statement的子接口,属于SQL预处理,与Statement不同的是PreparedStatement在操作时先在数据表中准备好一条待执行的SQL语句,后面再设置内容,这样的处理模型会让数据库操作更安全。

前面都是把变量拼凑到SQL中,当有大量的行要插入时,这样做效率既不高,也不方便维护,而且容易出错,这时就是PreparedStatement使用的场景了,它编写SQL用"?"进行占位符设计,Connection接口通过prepareStatement()方法来实例化PreparedStatement接口实例,在更新或查询时使用setXXX()方法依据占位符的索引顺序进行内容设置。

注意JDBC中的PreparedStatement,ResultSet操作的日期类型均为java.sql.Date,上面需要将java.util.Date转换为java.sql.Date类的实例,下面的类图说明了这个问题:

utilToSqlDate

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

class jdbcOper {
    String driverName = "org.mariadb.jdbc.Driver";
    String url = "jdbc:mysql://localhost:6612/test";
    String username = "root";
    String password = "666666";
    Connection connector = null;

    public jdbcOper() {
        try {
            Class.forName(driverName);
            // DriverManager.registerDriver(new Driver());
            connector = DriverManager.getConnection(url, username, password);
            System.out.println(connector);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    protected void finalize() throws Throwable {
        // TODO Auto-generated method stub
        super.finalize();
        connector.close();
    }

    public void InsertData() {
        String insertString = new StringBuilder(" insert into student ")
                .append(" (name,score,date) ")
                .append(" values(?,?,?) ")
                .toString();
        String names[] = { "xie", "wang", "li" };
        int scores[] = { 30, 40, 60 };

        try {
            PreparedStatement pStatement = connector.prepareStatement(insertString);

            for (int i = 0; i < names.length; i++) {
                pStatement.setString(1, names[i]);
                pStatement.setInt(2, scores[i]);
                pStatement.setDate(3, new java.sql.Date(new java.util.Date().getTime()));
                int count = pStatement.executeUpdate();
                System.out.println("插入行数:"+count);
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public void SelectData() {
        String fieldString="name";
        String valueString="xie";
        String selectString = new StringBuilder(" select count(*) from student ")
                .append(" where "+fieldString+" like ? ")
                .toString();
        PreparedStatement pStatement;
        try {
            pStatement = connector.prepareStatement(selectString);
            
            pStatement.setString(1, "%"+valueString+"%");
            ResultSet rs = pStatement.executeQuery();
            if(rs.next()) {
                System.out.println("符合条件的数量:"+rs.getLong(1));
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        jdbcOper jdbc = new jdbcOper();
        jdbc.InsertData();
        jdbc.SelectData();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

# 数据批处理

JDBC随着JDK每次版本更新也在更新,从JDK 2.0开始提供了更多的功能比如可滚动的结果集,使用结果集更新数据,批处理。下面是使用PreparedStatement执行批处理,它的返回值有3个:

  • 大于0的数字:SQL语句影响的行数。
  • Statement.SUCCESS_NO_INFO(-2):SQL执行成功。
  • Statement.EXECUTE_FAILED(-3):SQL执行失败。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

class jdbcOper {
    String driverName = "org.mariadb.jdbc.Driver";
    String url = "jdbc:mysql://localhost:6612/test";
    String username = "root";
    String password = "666666";
    Connection connector = null;

    public jdbcOper() {
        try {
            Class.forName(driverName);
            // DriverManager.registerDriver(new Driver());
            connector = DriverManager.getConnection(url, username, password);
            System.out.println(connector);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    public void insertData() {
        String insertString = new StringBuilder(" insert into student ")
                .append(" (name,score,date) ")
                .append(" values(?,?,?) ")
                .toString();
        String names[] = { "xie", "wang", "li" };
        int scores[] = { 30, 40, 60 };

        try {
            PreparedStatement pStatement = connector.prepareStatement(insertString);

            for (int i = 0; i < names.length; i++) {
                pStatement.setString(1, names[i]);
                pStatement.setInt(2, scores[i]);
                pStatement.setDate(3, new java.sql.Date(new java.util.Date().getTime()));
                pStatement.addBatch();
            }
            pStatement.executeBatch();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    protected void finalize() throws Throwable {
        // TODO Auto-generated method stub
        super.finalize();
        connector.close();
    }

}

public class Main {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        jdbcOper jdbc = new jdbcOper();
        jdbc.insertData();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

# 事务控制

事务指的是一组操作,要么一起成功,要么一起失败。事务具有以下特性(ACID):

  • 原子性:是事务的最小单元,不能再分割,这些操作必须要么一起成功,要么一起失败。A给B转账,如果A转账失败,B就不能收到钱。
  • 一致性:如果数据库操作失败,则其前后是一致的。A给B转账,如果转账失败,A的钱不减少,B的钱不增加。
  • 隔离性:多个事务可同时进行且彼此无法访问,只有事务最后完成时才能看到结果。
  • 持久性:当系统崩溃时,事务依然可以坚持提交,当事务完成后,操作的结果会保存在磁盘中。

JDBC默认开启了事务的自动提交模式。

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

class jdbcOper {
    String driverName = "org.mariadb.jdbc.Driver";
    String url = "jdbc:mysql://localhost:6612/test";
    String username = "root";
    String password = "666666";
    Connection connector = null;
    Statement statement;

    public jdbcOper() {
        try {
            Class.forName(driverName);
            // DriverManager.registerDriver(new Driver());
            connector = DriverManager.getConnection(url, username, password);
            System.out.println(connector);
            statement = connector.createStatement();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    public void UseTransaction() {
        try {
            connector.setAutoCommit(false);
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            statement.addBatch("insert into student(name,score) values ('testName1',100)");
            statement.addBatch("delete from student where learn_id=h");
            int result[]=statement.executeBatch();
            connector.commit();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            try {
                System.out.println("操作失败进行回滚!");
                connector.rollback();
            } catch (SQLException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }
        
    }
    

    @Override
    protected void finalize() throws Throwable {
        // TODO Auto-generated method stub
        super.finalize();
        connector.close();
    }

}

public class Main {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        jdbcOper jdbc = new jdbcOper();
        jdbc.UseTransaction();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

# 数据库连接池

上面的数据库操作都是单个连接,每次做完操作后都会关闭数据库,当有大量数据库操作时这样做效率很低,浪费资源。这时可使用数据库连接池,我们可以在数据库连接池中放若干个数据库连接,当需要数据库操作时,从这些连接中选一个给操作对象,操作完成后再把这个连接归还数据库连接池,这样做可大大提高效率,避免了因为大量数据库连接而频繁地申请资源。

javax.sql.DataSource提供标准接口,有两个常用操作:

  • 获取连接:getConnection()。
  • 归还连接:如果Connection是从连接池获取,调用Connection.close()方法,则不会再关闭数据库连接,而是把该Connection归还数据库连接池。

这里研究就两种数据库连接池实现:

  1. C3P0:hibernate常用该技术
  2. Druid:由阿里巴巴提供的解决方案

# C3P0

c3p0是让JDBC驱动面向企业开发的库,之前支持jdbc3和jdbc2,版本0.9.5支持jdbc4。它提供服务有:

  • 在获取数据库连接时将传统基于DriverManager的JDBC驱动程序适配到javax.sql.DataSource的一个类。
  • 对数据库源透明(无论内部如何实现,对外提供的接口是一样的)的Connection和PreparedStatements连接池,他能包装基于传统驱动或未加入连接池的数据源。

那么如何使用C3P0呢?

  1. 导入包:

    • c3p0-0.9.5.5.jar
    • mchange-commons-java-0.2.19.jar
    • mariadb-java-client-2.6.2.jar
  2. 定义配置文件(c3p0-config.xml),同下面测试java代码放在一块:

    <?xml version="1.0" encoding="UTF-8"?>
    <c3p0-config>
    <!-- 使用默认的配置读取连接池对象 -->
    <default-config>
        <!--  连接参数 -->
        <property name="driverClass">org.mariadb.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://10.0.10.150:6612/test</property>
        <property name="user">root</property>
        <property name="password">6666666</property>
    
        <!-- 连接池参数 -->
        <!--初始化申请的连接数量-->
        <property name="initialPoolSize">5</property>
        <!--最大的连接数量-->
        <property name="maxPoolSize">3</property>
        <!--超时时间-->
        <property name="checkoutTimeout">1000</property>
    </default-config>
    
    <named-config name="c3p0driver"> 
        <!--  连接参数 -->
        <property name="driverClass">org.mariadb.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://10.0.10.150:6612/test</property>
        <property name="user">root</property>
        <property name="password">666666</property>
    
        <!-- 连接池参数 -->
        <property name="initialPoolSize">2</property>
        <property name="maxPoolSize">10</property>
        <property name="checkoutTimeout">1000</property>
    </named-config>
    </c3p0-config>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
  3. 源码中使用C3P0

    import java.sql.Connection;
    import javax.sql.DataSource;
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            //1.  获取DataSource
            DataSource ds  = new ComboPooledDataSource("c3p0driver");
            //DataSource ds  = new ComboPooledDataSource();
            int connectionSize=11;
            Connection conn[]= new Connection[connectionSize]; 
            //2. 获取连接
            for (int i = 0; i < connectionSize ; i++) {
                conn[i] = ds.getConnection();
                System.out.println(i+":"+conn[i]);
                if(i==6) {
                    System.out.println("connect 6 is closed!");
                    conn[i].close();
                }
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
  4. 注意Eclipse环境中更改c3p0-config.xml中的配置,需要Project->Clean清除下,修改的配置才能生效。在windows文件系统中修改文件名需要在Eclipse中的workspace上刷新才能起作用。

# Druid

Druid是Java中数据库连接池,由阿里集团创建和维护,Druid能够提供强大的监控和扩展功能。

使用步骤:

  1. 导入包:

    • druid-1.2.1.jar
    • mariadb-java-client-2.6.2.jar
  2. 定义配置文件(MyDruidConfig.properties):

    driverClassName=org.mariadb.jdbc.Driver
    url=jdbc:mysql://10.0.10.150:6612/test
    username=root
    password=666666
    initialSize=5
    maxActive=5
    maxWait=3000
    
    1
    2
    3
    4
    5
    6
    7
  3. java中使用druid库:

    import java.io.InputStream;
    import java.sql.Connection;
    import java.util.Properties;
    import javax.sql.DataSource;
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            // 获取配置文件
            Properties pro = new Properties();
            InputStream is = Main.class.getClassLoader().getResourceAsStream("MyDruidConfig.properties");
            pro.load(is);
            // 获取数据源
            DataSource ds = DruidDataSourceFactory.createDataSource(pro);
            int connectionSize=6;
            Connection conn[]= new Connection[connectionSize];
    
            //测试连接
            for (int i = 0; i < connectionSize ; i++) {
                conn[i] = ds.getConnection();
                System.out.println(i+":"+conn[i]);
                if(i==3){
                    System.out.println("connect 6 is closed!");
                    conn[i].close();
                }
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28

# 基于Druid工具类

DruidJDBCUtils.java文件内容:

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class DruidJDBCUtils {

    private static DataSource ds ;

    static{
        try {
            //1.加载配置文件
            Properties pro = new Properties();
            pro.load(DruidJDBCUtils.class.getClassLoader().getResourceAsStream("MyDruidConfig.properties"));
            //2.获取DataSource
            ds = DruidDataSourceFactory.createDataSource(pro);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }


    public static void close(Statement stmt,Connection conn){
       close(null,stmt,conn);
    }


    public static void close(ResultSet rs , Statement stmt, Connection conn){


        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();
            }
        }
    }

    public static DataSource getDataSource(){
        return  ds;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

测试上面工具类:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class Main {
    public static void main(String[] args) throws Exception {
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = DruidJDBCUtils.getConnection();
            String sql = "insert into person values(?,?)";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1,"小王");
            pstmt.setInt(2,20);
            int count = pstmt.executeUpdate();
            System.out.println(count);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DruidJDBCUtils.close(pstmt,conn);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# SpringJDBC

上面使用java的JDBC有一些操作会比较繁琐,比如连接数据库操作,查询数据时获取每列数据,SpringJDBC提供了JDBC Template技术,封装了JDBC,能大大简化这些操作。

# 使用步骤

基本操作流程如下:

  1. 下载spring包 (opens new window)

    • spring-jdbc-5.2.9.RELEASE.jar
    • spring-beans-5.2.9.RELEASE.jar
    • spring-core-5.2.9.RELEASE.jar
    • spring-tx-5.2.9.RELEASE.jar
    • commons-logging-1.2.jar
  2. 测试Spring JDBC连接:

    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    
    import org.springframework.jdbc.core.JdbcTemplate;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            JdbcTemplate template = new JdbcTemplate(DruidJDBCUtils.getDataSource());
            //3.调用方法
            String sql = "update person set age = 33 where name = ?";
            int count = template.update(sql, "wang");
            System.out.println(count);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

# 基本操作

Spring JDBC有两个基本操作:

  1. 获取jdbc:

    JdbcTemplate template = new JdbcTemplate(datasource);

  2. 完成CRUD的操作:
    • update():执行DML语句。增、删、改语句
    • queryForMap():查询结果将结果集封装为map集合,将列名作为key,将值作为value 将这条记录封装为一个map集合,这个方法查询的结果集长度只能是1。
    • queryForList():查询结果将结果集的每一条记录封装为一个Map集合,再将Map集合装载到List集合中
    • query(RowMapper):查询结果,将结果封装为JavaBean对象
    • queryForObject:查询结果,将结果封装为对象

# 练习使用

import cn.itcast.utils.JDBCUtils;
import org.junit.Test;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

public class JdbcTemplateDemo {

    //Junit单元测试,可以让方法独立执行


    //1. 获取JDBCTemplate对象
    private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
    /**
     * 1. 修改1号数据的 salary 为 10000
     */
    @Test
    public void test1(){

        //2. 定义sql
        String sql = "update emp set salary = 10000 where id = 1001";
        //3. 执行sql
        int count = template.update(sql);
        System.out.println(count);
    }

    /**
     * 2. 添加一条记录
     */
    @Test
    public void test2(){
        String sql = "insert into emp(id,ename,dept_id) values(?,?,?)";
        int count = template.update(sql, 1015, "郭靖", 10);
        System.out.println(count);

    }

    /**
     * 3.删除刚才添加的记录
     */
    @Test
    public void test3(){
        String sql = "delete from emp where id = ?";
        int count = template.update(sql, 1015);
        System.out.println(count);
    }

    /**
     * 4.查询id为1001的记录,将其封装为Map集合
     * 注意:这个方法查询的结果集长度只能是1
     */
    @Test
    public void test4(){
        String sql = "select * from emp where id = ? or id = ?";
        Map<String, Object> map = template.queryForMap(sql, 1001,1002);
        System.out.println(map);
        //{id=1001, ename=孙悟空, job_id=4, mgr=1004, joindate=2000-12-17, salary=10000.00, bonus=null, dept_id=20}

    }

    /**
     * 5. 查询所有记录,将其封装为List
     */
    @Test
    public void test5(){
        String sql = "select * from emp";
        List<Map<String, Object>> list = template.queryForList(sql);

        for (Map<String, Object> stringObjectMap : list) {
            System.out.println(stringObjectMap);
        }
    }

    /**
     * 6. 查询所有记录,将其封装为Emp对象的List集合
     */

    @Test
    public void test6(){
        String sql = "select * from emp";
        List<Emp> list = template.query(sql, new RowMapper<Emp>() {

            @Override
            public Emp mapRow(ResultSet rs, int i) throws SQLException {
                Emp emp = new Emp();
                int id = rs.getInt("id");
                String ename = rs.getString("ename");
                int job_id = rs.getInt("job_id");
                int mgr = rs.getInt("mgr");
                Date joindate = rs.getDate("joindate");
                double salary = rs.getDouble("salary");
                double bonus = rs.getDouble("bonus");
                int dept_id = rs.getInt("dept_id");

                emp.setId(id);
                emp.setEname(ename);
                emp.setJob_id(job_id);
                emp.setMgr(mgr);
                emp.setJoindate(joindate);
                emp.setSalary(salary);
                emp.setBonus(bonus);
                emp.setDept_id(dept_id);

                return emp;
            }
        });


        for (Emp emp : list) {
            System.out.println(emp);
        }
    }

    /**
     * 6. 查询所有记录,将其封装为Emp对象的List集合
     */

    @Test
    public void test6_2(){
        String sql = "select * from emp";
        List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
        for (Emp emp : list) {
            System.out.println(emp);
        }
    }

    /**
     * 7. 查询总记录数
     */

    @Test
    public void test7(){
        String sql = "select count(id) from emp";
        Long total = template.queryForObject(sql, Long.class);
        System.out.println(total);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

上面的Emp对象定义为:

import java.util.Date;

public class Emp {
    private Integer id;
    private String ename;
    private Integer job_id;
    private Integer mgr;
    private Date joindate;
    private Double salary;
    private Double bonus;
    private Integer dept_id;


    public Integer getId() {
        return id;
    }

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

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Integer getJob_id() {
        return job_id;
    }

    public void setJob_id(Integer job_id) {
        this.job_id = job_id;
    }

    public Integer getMgr() {
        return mgr;
    }

    public void setMgr(Integer mgr) {
        this.mgr = mgr;
    }

    public Date getJoindate() {
        return joindate;
    }

    public void setJoindate(Date joindate) {
        this.joindate = joindate;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    public Double getBonus() {
        return bonus;
    }

    public void setBonus(Double bonus) {
        this.bonus = bonus;
    }

    public Integer getDept_id() {
        return dept_id;
    }

    public void setDept_id(Integer dept_id) {
        this.dept_id = dept_id;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "id=" + id +
                ", ename='" + ename + '\'' +
                ", job_id=" + job_id +
                ", mgr=" + mgr +
                ", joindate=" + joindate +
                ", salary=" + salary +
                ", bonus=" + bonus +
                ", dept_id=" + dept_id +
                '}';
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

# ORM

ORM为Object Relational Mapping,是一种从面向对象到关系数据的转变,是操作数据库的中间环节,目前的ORM有MyBatis,Hibernate,ADO.NET Entity Framework。

面向对象和关系数据库的概念对应关系是怎样的呢?

面向对象概念 数据库概念
类成员属性 表中字段
对象引用关联 表的外键关联
类的一个实例化对象 表的一行记录
对象数组 表的多行记录