Java JDBC

 

1、什麼是JDBC?

  JDBC就是Java連接數據庫的一種方式,一種規則。java

2、爲何要學JDBC?

  由於你的Java項目須要連接數據庫保存數據。目前來講,JDBC是最底層的東西,當前市面上流行的最火的JDBC封裝有hibernate和mybatis,這倆均可以簡化一些操做。其實他倆底層仍是JDBC,就是作了個封裝,讓人使用更簡單而已。爲了深刻了解hibernate和mybatis,JDBC仍是要學的。sql

3、執行增刪改語句

  先來嘗試一條語句:數據庫

package com.StadyJava.day2;

import org.junit.*;

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

public class JDBCDemo {

    @Test
    public void Con() throws Exception {

        String sql="insert SysUser values('201408090009',123,'李信','男','王者榮耀','shuyunquan@qq.com','老師')";

        //1.加載註冊Mysql驅動
        Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
        //2.連接數據庫,獲取連接對象
        Connection con = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;databaseName=Design;user=sa;password=123");
        //3.建立語句對象
        Statement st=con.createStatement();
        //4.執行SQL語句
        int row=st.executeUpdate(sql);
        //5.釋放資源
        st.close();
        con.close();
        System.out.println(row);

    }


}

  是能夠成功的,這裏我使用的Jnuit測試單元來作的,不是Main方法,這個之前的博客介紹過。還有JDBC的SQL Server連接包,在Maven裏面下載就行了。Maven不會的本身學。apache

executeUpdate方法能夠執行增刪改和建立表的語句。緩存

 

4、執行查詢語句

  增刪改完成了,如今來看看查詢語句是怎麼寫的,首先要知道,JDBC查詢會返回一個結果集 ResultSet 這個結果集就像一個遊標同樣,咱們能夠逐層訪問他裏面的內容。服務器

首先我寫一個SQL語句mybatis

select Name,Sex from SysUser 

我查詢兩個字段,內容是這樣的dom

Name  Sex
許嵩    男
林俊杰  男
陳亮    男
繆斯    女
魁拔    女
範鎖    男
李信    男

看看代碼,換成 executeQuery 了ide

package com.StadyJava.day2;

import org.junit.*;

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

public class JDBCDemo {

    @Test
    public void Con() throws Exception {

        String sql="select Name,Sex from SysUser";

        //1.加載註冊Mysql驅動
        Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
        //2.連接數據庫,獲取連接對象
        Connection con = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;databaseName=Design;user=sa;password=123");
        //3.建立語句對象
        Statement st=con.createStatement();
        //4.執行SQL語句
        ResultSet rs=st.executeQuery(sql);

        //處理結果集
        while (rs.next()){
            String name=rs.getString("Name");
            String sex=rs.getString("Sex");
            System.out.println(name+','+sex);
        }

        //5.釋放資源
        rs.close();
        st.close();
        con.close();

    }


}

 

上面的都是最簡單最簡單的JDBC操做,工做中咱們徹底不會這樣去操做,去寫。就好比這個增刪改,寫在一個方法裏面,那咱們多處使用這個,豈不是每一個方法都要寫?sqlserver

這樣代碼就重複了,違背了ORP單一原則。.Net裏面是有類庫這一律唸的,就是封裝成一個類庫,使用的時候調用就能夠了。

Java這裏咱們也來對JDBC進行一個封裝。

 

1、建立操做接口

寫接口比較規範,我規定你對個人這個類進行的操做就是增刪改查,下面寫實現類

package com.StadyJava.DAODemo.dao;
import com.StadyJava.DAODemo.domain.User;
import java.util.List;

public interface IUserDAO {
    /**
     * 保存操做
     * @param user
     */
    void save(User user);

    /**
     * 刪除操做
     * @param id
     */
    void delete(Long id);

    void update(Long id,User newuser);

    User get(Long id);

    List<User> listAll();

}

能夠看到,個人接口寫了註釋,/** 而後敲回車就是對方法的註釋,這樣有一個好處,就是個人實現類,你在寫的時候,鼠標放上去,會有提示。

 

2、建立Model類

我建立了一個User的Model類:

package com.StadyJava.DAODemo.domain;

import lombok.Getter;
import lombok.Setter;

@Setter@Getter
public class User {
    private long id;
    private String name;
    private String sex;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex=" + sex +
                '}';
    }
}

我使用的Lombok自動建立的屬性構造器

 

 

3、鏈接池

什麼是鏈接池?能夠看到,咱們上面是直接寫了一個Connection,使用完畢以後直接釋放的。這樣很差,由於Connection鏈接建立很是耗費資源,你用一下就釋放了,再用再鏈接,這樣不行的。因此鏈接池的概念就出來了,鏈接池裏有默認的幾個Connection,誰用誰來獲取,使用完成以後釋放,這個釋放的意思是把Connection資源歸還給鏈接池,並無真正的釋放。這樣保證了效率。

 

 

鏈接池有兩種是須要介紹一下的

1.Apache作的DBCP

2.阿里作的號稱世界上最快的druid

 

先來介紹DBCP鏈接池

package com.StadyJava.DAODemo.util;

import com.StadyJava.DAODemo.JunitDAO; import org.apache.commons.dbcp2.BasicDataSourceFactory; import javax.sql.*; import java.sql.*; import java.util.Properties; public class DBCPUtil { //建立一個鏈接池對象,由於個人鏈接池對象只須要建立一次便可,因此我寫在靜態代碼塊裏 private static DataSource ds=null; static{ Properties properties=new Properties(); try { properties.load(DBCPUtil.class.getClass().getResourceAsStream("/test")); ds=BasicDataSourceFactory.createDataSource(properties); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConn(){ try { return ds.getConnection(); } catch (Exception e) { e.printStackTrace(); } return null; } //釋放資源 public static void close(Connection conn, Statement st, ResultSet rs) { try{ if (rs != null) { rs.close(); } }catch (Exception e){ e.printStackTrace(); }finally { try{ if (st != null) { st.close(); } }catch (Exception e){ e.printStackTrace(); }finally { try{ if (conn != null) { conn.close(); } }catch (Exception e){ e.printStackTrace(); } } } } }

而後在Junit測試寫了一個調用的方法:

    @Test
    public void DBCPTest() throws Exception { //測試連接池 Connection conn=DBCPUtil.getConn(); //這裏的SQL暫時先寫死 PreparedStatement ps=conn.prepareStatement("select AccountNumber from SysUser "); ResultSet rs=ps.executeQuery(); while (rs.next()) { System.out.println(rs.getLong("AccountNumber")); } DBCPUtil.close(conn,ps,rs); }

 

 

運行結果:

 

下面是DruId鏈接池,其實都差很少 

package com.StadyJava.DAODemo.util;

import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidDataSourceFactory; import org.apache.commons.dbcp2.BasicDataSourceFactory; import javax.sql.DataSource; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import java.util.Properties; public class DruidUtil { //建立一個鏈接池對象,由於個人鏈接池對象只須要建立一次便可,因此我寫在靜態代碼塊裏 private static DataSource ds=null; static{ Properties properties=new Properties(); try { properties.load(DBCPUtil.class.getClass().getResourceAsStream("/test")); ds=DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConn(){ try { return ds.getConnection(); } catch (Exception e) { e.printStackTrace(); } return null; } //釋放資源 public static void close(Connection conn, Statement st, ResultSet rs) { try{ if (rs != null) { rs.close(); } }catch (Exception e){ e.printStackTrace(); }finally { try{ if (st != null) { st.close(); } }catch (Exception e){ e.printStackTrace(); }finally { try{ if (conn != null) { conn.close(); } }catch (Exception e){ e.printStackTrace(); } } } } }

 

 

這裏面咱們的Statement換成了PreparedStatement,由於PreparedStatement能夠去拼接SQL,是動態的SQL,statement僅僅是靜態的。

 

PreparedStatement和statement均可以表示語句對象:

Preparedstatement相對於statement的優點:

1):拼接SQL上,操做更簡單. 
2):性能會更加高效,可是須要取決於數據庫服務器是否支持.
MySQL:不支持 Oracle:支持

 

 4、建立JDBC的模板

由於增刪改的操做大部分都是同樣的,因此創建一個模板比較好,這個模板裏面就兩個方法,一個是update,主要是增刪改。一個是queue,主要是查詢。

這裏我寫了T ,自定義泛型,主要是傳入的類型是什麼,我獲取的返回類型就是什麼。 

 

package com.StadyJava.DAODemo.util;

import com.StadyJava.DAODemo.dao.IResultSetHandler;

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

public class JDBCTemplate {

    /**
     * 操做增刪改的模板
     * @param sql
     * @param params
     * @return
     */
    public static int update(String sql,Object... params){
        Connection conn=null;
        PreparedStatement ps=null;

        try {
            conn=DruidUtil.getConn();
            ps=conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1,params[i]);
            }
            return ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            DruidUtil.close(conn,ps,null);
        }
        return 0;
    }

    /**
     * 操做查詢的模板
     * @param sql
     * @param params
     * @return
     */
    public static <T>T queue(String sql, IResultSetHandler<T> rsh, Object... params) {
        Connection conn=null;
        PreparedStatement ps=null;
        ResultSet rs=null;

        try {
            conn=DruidUtil.getConn();
            ps=conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1,params[i]);
            }
            rs=ps.executeQuery();
            return  rsh.handle(rs);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            DruidUtil.close(conn,ps,rs);
        }
        throw new RuntimeException("查詢結果有錯誤");
    }

}

 

5、建立接口的實現類

咱們第一步寫了一個操做的規範接口,如今來實現一下

package com.StadyJava.DAODemo.dao.impl;

import com.StadyJava.DAODemo.dao.IResultSetHandler;
import com.StadyJava.DAODemo.dao.IUserDAO;
import com.StadyJava.DAODemo.domain.User;
import com.StadyJava.DAODemo.util.JDBCTemplate;
import com.StadyJava.DAODemo.util.JDBCUtil;

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

public class UserDAOImpl implements IUserDAO {

    @Override
    public void save(User user) {
        JDBCTemplate.update("insert SysUser (AccountNumber,Name,Sex) values(?,?,?)",user.getId(),user.getName(),user.getSex());
    }

    @Override
    public void delete(Long id) {
        JDBCTemplate.update("delete SysUser where AccountNumber=?,Name=?,Sex=?",id);
    }

    @Override
    public void update(Long id, User newuser) {
        JDBCTemplate.update("update SysUser set AccountNumber=?, Name=? ,Sex=? where AccountNumber=?",id,newuser.getId(),newuser.getName(),newuser.getSex());
    }

    @Override
    public User get(Long id) {
        return JDBCTemplate.queue("select * from SysUser  where AccountNumber=?",new UserResultSetHandler(),id);
    }

    @Override
    public List<User> listAll() {
        return JDBCTemplate.queue("select AccountNumber,Name,Sex from SysUser",new UserResultSetHandler());
    }


}

//結果集接口的實現類
class UserResultSetHandler implements IResultSetHandler<List<User>>
{
    @Override
    public List<User> handle(ResultSet rs) throws Exception {
        List<User> list=new ArrayList();
        while (rs.next()) {
            User user=new User();
            user.setId(rs.getLong("AccountNumber"));
            user.setName(rs.getString("Name"));
            user.setSex(rs.getString("Sex"));
            list.add(user);
        }
        return list;
    }
}

下面寫了一個查詢的結果集的實現類,能夠發現,個人 get()  方法實際上是報錯的,由於個人返回類型不一致了,這裏就牽涉出了另外一個問題,還要對查詢的結果集的實現類再次進行重構,可是這個重構使用了Java的內省機制,還要求你的User Model類的字段必須和數據庫裏面的字段一致,這裏我懶得改了,直接貼出代碼

 

package com.StadyJava.DAODemo.util;


import com.StadyJava.DAODemo.dao.IResultSetHandler;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.sql.ResultSet;

public class BeanHandler<T> implements IResultSetHandler<T> {

    private Class<T> classType;
    public BeanHandler(Class<T> classType){
        this.classType=classType;
    }

    @Override
    public T handle(ResultSet rs) throws Exception {
        //1.建立對應類的一個對象
        T obj=classType.newInstance();
        //2.使用內省機制取出數據
        BeanInfo beanInfo=Introspector.getBeanInfo(classType,Object.class);
        PropertyDescriptor [] pds=beanInfo.getPropertyDescriptors();

        if (rs.next()) {
            for (PropertyDescriptor pd : pds) {
                //獲取對象的屬性名
                String columnName=pd.getName();
                Object val=rs.getObject(columnName);
                //3.調用對象的setter方法
                pd.getWriteMethod().invoke(obj,val);
            }
        }
        return obj;
    }
}
package com.StadyJava.DAODemo.util;

import com.StadyJava.DAODemo.dao.IResultSetHandler;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class BeanListHandler<T> implements IResultSetHandler<List<T>> {

    private Class<T> classType;
    public BeanListHandler(Class<T> classType){
        this.classType=classType;
    }

    @Override
    public List<T> handle(ResultSet rs) throws Exception {

        List<T> list = new ArrayList<>();

        while (rs.next()) {
            //1.建立對應類的一個對象
            T obj = classType.newInstance();
            list.add(obj);

            //2.使用內省機制取出數據
            BeanInfo beanInfo = Introspector.getBeanInfo(classType, Object.class);
            PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();

            for (PropertyDescriptor pd : pds) {
                //獲取對象的屬性名
                String columnName = pd.getName();
                Object val = rs.getObject(columnName);
                //3.調用對象的setter方法
                pd.getWriteMethod().invoke(obj, val);
            }
        }
        return list;
    }

}

而後調用的時候,這樣寫就行了,這是終極的寫法

    @Override
    public User get(Long id) {
        return JDBCTemplate.queue("select * from SysUser  where AccountNumber=?",new BeanHandler<>(User.class),id);
    }

 

傳一個你的類進去,就能夠了,那個查詢結果集的實現類就能夠刪了

 

個人實現很簡單,就是使用Junit寫的實現類

    @Test
    public void testGetList() {
        System.out.println(userDAO.listAll());
    }

    @Test
    public void testGet() {
        System.out.println(userDAO.get(201408090001L));
    }

Junit很好用,你們必定要用起來。

 

 

 

還有一些其餘的知識須要瞭解一下:

 

 

事務

所謂的事務,理解以前先講一個例子。銀行轉帳的問題,好比我買許嵩的專輯,給許嵩轉帳。那麼我轉帳分爲幾個步驟:

1.個人帳戶錢減小

2.許嵩帳戶錢增長

3.完成

這三步必須是順序進行的,假如如今在第二步的時候斷電了,個人錢沒了,許嵩的錢卻沒有增長。這種狀況顯然是不合理的。因此事務的概念就出來了。所謂的事務,就是把幾個步驟當作一個。

只要有一個失敗,那麼所有失敗。必須所有成功,那纔算成功。

事務這個研究以後再另外寫一篇文章。如今這裏就簡單的介紹一下。

 下面是一些簡單的java代碼:

        //事務,手動寫一個
        Connection conn=null;
        //關閉事務的自動提交
        conn.setAutoCommit(false);
        //提交事務
        conn.commit();
        //回滾事務
        conn.rollback();

 

批處理

講一下什麼是批處理,在執行SQL語句的時候,目前都是一條一條的執行的,這樣是很麻煩的,效率也很低。能夠舉個例子瞭解一下。例如公交車,明明有200個座位,可是一次卻只拉一我的到目的地。

這樣假如我有3000人,那就要3000次。效率可謂是低下了。因此批處理就是一次執行多條語句。

公交車每次拉滿200人,這樣3000人只須要15次就完事了。這就是批處理的意義所在了。能夠看一下代碼是怎麼寫的,這裏只簡單的介紹一下語句。

 

        //批處理
        PreparedStatement ps=null;
        for (int i = 0; i < 1000; i++) {
            String sql="insert 表 values(?,?)";
            ps.addBatch();
            if (i%200 == 0) {
                ps.executeBatch();//執行批量操做
                ps.clearBatch();  //清空緩存
                ps.clearParameters();//清除參數
            }
        }

效率方面

Mysql JDBC 版本 5.1.13開始,效率提升了不少。版本舊的效率不咋滴。

相關文章
相關標籤/搜索