1. 程式人生 > >JDBC(資料庫的驅動、連線、java程式操作資料庫、事務、隔離級別、連線池等)

JDBC(資料庫的驅動、連線、java程式操作資料庫、事務、隔離級別、連線池等)

java操作資料庫的思想:連上資料庫,傳送sql語句。在連上資料庫之前,要先用程式啟動資料庫,因此,可以通過反射載入類驅動(com.jdbc.mysql.Driver)。通過驅動管理類的靜態方法傳遞資料庫的url來獲取一個連線物件(connection)。有三個過載的方法,第一個user和password都追加在url後(類似於get傳參);第二種用逗號將user和passowrd隔開作為第二個和第三個引數;第三種通過配置檔案Properties(方便修改,不用編譯)。連線物件獲取到表示資料庫連線成功。
1.連線資料庫。獲得連線物件

public class Maintest {
    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        Properties p=new Properties();
        p.load(new FileInputStream("E:\\IDEAJAVA\\Algorithm\\src\\test\\jdbc.properties"));
        Connection connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/test",p);
        System.out.println(connection);
    }
}

輸出結果

[email protected]

2.得到連線物件後就可以通過連線物件(connection)獲得傳送sql語句的物件(statement和PrepareStatement)。PrepareStatement相比Statement,可以進行預處理。就是先發送SQL語句,後傳送引數。傳送查詢語句用executeQuery方法,會返回一個Resultset物件。該物件封裝了查詢出的結果集;傳送其他語句可以用executeUpdate方法。該方法返回一個int型別的值,是影響了資料庫的條數。
建表前的資料庫:

mysql> use test
Database changed
mysql> show tables;
Empty set (0.00 sec)

建表的語句:

Statement statement=connection.createStatement();//獲得傳送語句的物件
String createTable="create table student(" //建立表的sql語句
      + "id int not null auto_increment primary key,"
      + "name varchar(20),"
      + "sex varchar not default 'M'"
      + ");";
statement.executeUpdate(createTable);//執行sql語句

建表後在次查詢收據庫,表已建好。

mysql> show tables;
Empty set (0.00 sec)

mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| student        |
+----------------+
1 row in set (0.00 sec)

3.插入資料:運用Statement和PrepareStatem兩種類。

String insertData1="insert into student values(1,'zhangsan','M')";//statement的sql
String insertData2="insert into student values(?,?,?)";
PreparedStatement preparedStatement=connection.prepareStatement(insertData2);
statement.executeUpdate(insertData1);//執行插入資料
//設定引數
preparedStatement.setInt(1,2);
preparedStatement.setString(2,"lisi");
preparedStatement.setString(3,"M");
System.out.println(preparedStatement.executeUpdate());//返回影響的行數

結果返回1。再看資料庫中的內容,插入成功。

mysql> select * from student;
+----+----------+-----+
| id | name     | sex |
+----+----------+-----+
|  1 | zhangsan | M   |
|  2 | lisi     | M   |
+----+----------+-----+
2 rows in set (0.00 sec)

4.查詢:通過傳送sql的物件(Statement和PreparStatemente),用executeQuery方法傳送查詢的sql語句,返回Resultset物件。executeUpdate方法不能傳送查詢語句,可以傳送建表,修改表結構,插入資料,刪除資料,修改資料。

Statement statement=connection.createStatement();
String query="select * from student";
ResultSet resultSet=statement.executeQuery(query);//返回查詢結果
while(resultSet.next())
{
    System.out.println("id:"+resultSet.getInt(1)+"\tname:"+resultSet.getString(2)+"\tsex:"+resultSet.getString(3));
}
System.out.println("--------------------------------------");
String querybyid="select * from student where id=?";
PreparedStatement preparedStatement=connection.prepareStatement(querybyid);
preparedStatement.setInt(1,1);//查詢id為1的學生的資訊。
ResultSet resultSet1=preparedStatement.executeQuery();
while(resultSet1.next())
{
    System.out.println("id:"+resultSet1.getInt(1)+"\tname:"+resultSet1.getString(2)+"\tsex:"+resultSet1.getString(3));
}

查詢結果顯示:

id:1	name:zhangsan	sex:M
id:2	name:lisi	sex:M
--------------------------------------
id:1	name:zhangsan	sex:M

5.批處理操作:通過addBatch方法向Statement物件中新增多個sql語句。再通過executeBatch方法執行每個SQL語句並返回每一條SQL語句執行後影響的行數。所以返回一個整型陣列。但是批處理中不能放查詢語句。

Statement statement=connection.createStatement();
statement.addBatch("insert into student values(3,\"luck\",\"F\");");
statement.addBatch("delete from student where id=1;");
System.out.println(Arrays.toString(statement.executeBatch()));

返回結果為1,1。資料庫查詢結果:

mysql> select * from student;
+----+------+-----+
| id | name | sex |
+----+------+-----+
|  2 | lisi | M   |
|  3 | luck | F   |
+----+------+-----+
2 rows in set (0.00 sec)

6.ResultSetMetaData類,該類封裝了一個查詢結果ResultSet的基本資訊,比如多少列、型別、建議名等。

Statement statement=connection.createStatement();
ResultSet resultSet=statement.executeQuery("select * from student");
ResultSetMetaData resultSetMetaData=resultSet.getMetaData();
for(int i=1;i<=resultSetMetaData.getColumnCount();i++)
{
    System.out.print(resultSetMetaData.getColumnName(i)+"\t\t");
}
System.out.println();
while(resultSet.next())
{
    System.out.println(resultSet.getInt(1)+"\t\t"+resultSet.getString(2)+"\t\t"+resultSet.getString(3));
}

查詢結果:

id		name		sex		
2		lisi		M
3		luck		F

7.事務:jdbc本身不支援事務,jdbc只是對事務做了簡單的封裝。還是用了資料庫的事務。
事務四大特性:A(原子性)、C(一致性)、I(隔離性)、D(永續性)。
髒讀:事務A讀到了事務B修改單未提交的資料。
幻讀:事務A第一次讀到了符合條件的資料,事務B又添加了幾條符合條件的資料,事務A再次讀時,比第一次讀到的多。出現幻讀。
不可重複讀:事務A讀到資料後,事務B對資料進行修改,事務A再次讀資料發現兩次結果不一樣。
針對以上三個問題出現以下四種隔離級別:
序列化(Serializable):級別最高,對事務序列執行,耗資源最大
重複讀(repeatable read):保證了一個事務不會修改另一個事務已經讀到的資料,避免了髒讀和不可重複讀。是大多數系統預設的隔離級別。
提交讀(read commit):保證了一個事務不會讀到一個已經修改但是還未提交的資料。只避免了髒讀。
為提交讀(read uncommitted):一個事務中的修改即使沒有提交,其他事務也能查詢到。一下是對事務的操作:

connection.setAutoCommit(false);//開啟事務
try {
    statement.executeUpdate("insert into student values(4,\"abc\",\"M\");");
    statement.executeUpdate("delete from student where id=5;");//庫中沒有id=5的學生。出現異常
    connection.commit();//未出現異常時提交
} catch (SQLException e) {
    connection.rollback();//出現異常時撤回所有的操作
}finally {
    connection.close();
}

因為出現異常,所有撤回所有的操作,因此在表中不能查到“abc”。將刪除的id改為2後繼續執行,這次不會出現異常,因此兩句都被執行並提交。

mysql> select * from student;
+----+------+-----+
| id | name | sex |
+----+------+-----+
|  3 | luck | F   |
|  4 | abc  | M   |
+----+------+-----+
2 rows in set (0.00 sec)

msyql支援四種隔離級別,隔離級別的檢視與設定:
檢視全域性和會話事務的隔離級別

mysql> select @@global.tx_isolation,@@tx_isolation;
+-----------------------+-----------------+
| @@global.tx_isolation | @@tx_isolation  |
+-----------------------+-----------------+
| REPEATABLE-READ       | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.00 sec)

修改隔離級別:

mysql> set tx_isolation="serializable";
Query OK, 0 rows affected (0.11 sec)

mysql> select @@global.tx_isolation,@@tx_isolation;
+-----------------------+----------------+
| @@global.tx_isolation | @@tx_isolation |
+-----------------------+----------------+
| REPEATABLE-READ       | SERIALIZABLE   |
+-----------------------+----------------+
1 row in set (0.00 sec)

8.連線池:連線池記憶體放多個連線物件,當程式中需要用到時從中取出即可。不需要時,歸還給連線池。實現連線物件的複用。連線池就是用來管理連線物件的。連線池要實現DataSource介面,該介面中有一個getConnection方法獲得連線物件
c3p0連線池:下載c3p0的jar包,匯入jar包。在src目錄下加入c3p0.properties
配置檔案。

public class Maintest {
    public static void main(String[] args) throws Exception {
        DataSource ds=new ComboPooledDataSource();
       for(int i=0;i<10;i++)
       {
           Connection con=ds.getConnection();
           System.out.println(con);
           con.close();//將連線物件歸還給連線池
       }
    }
}

配置檔案內容

c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql://localhost:3306/test
c3p0.user=root
c3p0.password=mysql
c3p0.maxPoolSize=10
c3p0.minPoolSize=3

Druid連線池:下載並匯入jar包。Druid相比於c3p0來說,更加靈活。既可以通過配置檔案來讀連線資料庫的資訊,也可以通過程式來設定。

public class Maintest {
    public static void main(String[] args) throws Exception {
       DruidDataSource ds=new DruidDataSource();
       //通過程式設定
       ds.setDriverClassName("com.mysql.jdbc.Driver");
       ds.setUrl("jdbc:mysql://localhost:3306/test");
       //通過配置檔案來設定。兩種方式
//        ResourceBundle rs=ResourceBundle.getBundle("jdbc");
//        ds.setUsername(rs.getString("user"));
//        ds.setPassword(rs.getString("password"));
        Properties properties=new Properties();
        properties.load(new FileReader("E:\\IDEAJAVA\\Algorithm\\src\\jdbc.properties"));
        ds.setUsername(properties.getProperty("user"));
        ds.setPassword(properties.getProperty("password"));
        ds.setInitialSize(3);
        ds.setMaxActive(10);
        Connection con=ds.getConnection();
        System.out.println(con);
    }
}

以上程式碼建立了Druid連線池,通過程式設定了mysql的驅動,以及mysql的url。通過兩種讀取配置檔案的方式來獲取使用者名稱和密碼,再將其設定到連線池物件中。最後再通過程式設定連線池的初始容量以及最大個數。通過連線池獲取物件,將物件輸出。