java操作數據庫的幾種方式


歷史就是一面鏡子

回顧自己開發的歷程,見證了時代變遷史記,下面我針對java連接數據庫的方式說起

0 原生jdbc

先普及下jdbc,怕新入行的人早已沉浸在包裝庫和框架中,甚至都沒用過原生jdbc。

Java數據庫連接,全稱是Java Database Connectivity,簡稱JDBC,是Java語言中用來規范客戶端程序如何來訪問數據庫的應用程序接口,提供了諸如查詢和更新數據庫中數據的方法。JDBC也是Sun Microsystems的商標。我們通常說的JDBC是面向關系型數據庫的。百科這樣解釋的Java數據庫連接

開發人員要做的幾個步驟:

開發步驟:

  1、注冊驅動.,告知JVM使用的是哪一個數據庫的驅動

  2、獲得連接.,使用JDBC中的類,完成對MySQL數據庫的連接

  3、獲得語句執行平台,通過連接對象獲取對SQL語句的執行者對象

  4、執行sql語句,使用執行者對象,向數據庫執行SQL語句  獲取到數據庫的執行后的結果

  5、處理結果

  6、釋放資源.

注意寫代碼之前,要導入數據庫驅動包,連接不同廠商的數據庫要用不同的驅動包


對應的驅動包 


新建項目,普通的java項目就行,導入第三方jar太簡單了,自行百度

示例代碼如下:

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;

import com.mysql.jdbc.ResultSetMetaData;

/**
 * 
 * @author dgm
 * @describe "原生jdbc"
 * @date 2020年4月13日
 */
public class MysqlTest {
    // JDBC 驅動名及數據庫 URL
    static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
    static final String DB_URL = "jdbc:mysql://192.168.8.200:3306/bdrackdemo?useUnicode=true&characterEncoding=utf8&autoReconnect=true";
    // 數據庫的用戶名與密碼,需要根據自己的設置
    static final String USER = "root";
    static final String PASS = "cstorfs";
    static Properties prop = new Properties();

    //讀取數據庫配置文件
    static void readDBSetting(String path) {
        // Properties prop = new Properties();
        // 讀取屬性文件mysql.properties
        InputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(path));
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            prop.load(in);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } // /加載屬性列表
        Iterator<String> it = prop.stringPropertyNames().iterator();
        while (it.hasNext()) {
            String key = it.next();
            System.out.println(key + "=" + prop.getProperty(key));
        }
        try {
            in.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // return prop;
    }

    public static void main(String[] args) {
        // 讀取mysql 配置信息
        readDBSetting("conf/mysql.properties");

        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            // 注冊 JDBC 驅動
            Class.forName(prop.getProperty("dbDriver")).newInstance();

            // 打開鏈接
            System.out.println("連接數據庫...");
            conn = DriverManager
                    .getConnection(
                            "jdbc:mysql://"
                                    + prop.getProperty("mysqlhost")
                                    + ":"
                                    + prop.getProperty("mysqlport")
                                    + "/"
                                    + prop.getProperty("dbname")
                                    + "?useUnicode=true&characterEncoding=utf8&autoReconnect=true",
                            prop.getProperty("mysqluser"),
                            prop.getProperty("mysqlpwd"));

            // 執行查詢
            System.out.println(" 實例化Statement對象...");
            stmt = conn.createStatement();
            String sql = "SELECT id, username, number FROM student";
            rs = stmt.executeQuery(sql);

            // 展開結果集數據庫
            while (rs.next()) {
                // 輸出數據
                System.out.print("用戶id: " + rs.getInt("id"));
                System.out.print(", 用戶名: " + rs.getString("username"));
                System.out.print(", 學號: " + rs.getString("number"));
                System.out.print("\n");
            }
        } catch (SQLException se) {
            // 處理 JDBC 錯誤
            se.printStackTrace();
        } catch (Exception e) {
            // 處理 Class.forName 錯誤
            e.printStackTrace();
        } finally {
            // 關閉資源
            try {
                if (rs != null)
                    rs.close();
            } catch (SQLException se3) {
            }// 什么都不做
            // 關閉資源
            try {
                if (stmt != null)
                    stmt.close();
            } catch (SQLException se2) {
            }// 什么都不做
            try {
                if (conn != null)
                    conn.close();
            } catch (SQLException se) {
                se.printStackTrace();
            }
        }
    }
}
View Code

 

查詢結果

恩早期開發就是這么過來,只是自己封裝成了JDBC工具,於具體的業務開發人員要寫大量的SQL語句,調用時這樣的


 看上去此種開發模式也很好,只要規模不大,此方式還很好,瓶頸在於數據庫連接,現在也不是問題了,社會在進步,池化技術出現了。

池化簡單點說就是預先連接好一定數量的連接,等需要時隨意從中選擇一個進行操作。略了,當時主要用的tomcat自帶的池化技術,此時獲取數據庫連接的代碼就變成了這樣核心偽代碼(tomcat要做些配置):

//構造函數
    public DataAccess(String poolName) 
    {
        this.poolName = poolName;
        
        this.JNDI = "java:comp/env/" + poolName;
    }

    //獲取數據庫連接
    private void setConnection() throws Exception 
    {
        Config config = Config.getConfig();

        if ( config.getWebserver().equals("tomcat")  )
        {
            Context ctx = new InitialContext();

            DataSource ds = (DataSource)ctx.lookup(JNDI);
           
            //獲取數據庫連接
            this.conn = ds.getConnection();
        } else
         {
        //其他server
       }
        
    }

普及下JNDIhttps://baike.baidu.com/item/JNDI/3792442?fr=aladdin,現在也難見到了,很多框架越來越傻瓜式了

 

1.  hibernate來了

Hibernate是一種ORM框架,全稱為 Object_Relative DateBase-Mapping,在Java對象與關系數據庫之間建立某種映射,以實現直接存取Java對象。

想不明白為啥沒有mybatis發展的好,看來拉攏技術人員搞社區還是很重要的。

很遺憾,沒有保留以前的老項目,沒有自己的電腦,也沒有申請github。

看了下網上已有很多教程了:https://www.w3cschool.cn/hibernate/skzl1idz.html,就不多說什么,重點關注架構、緩存、事務和攔截器。重點代碼

  //獲取加載配置管理類
        Configuration configuration = new Configuration();

        //不給參數就默認加載hibernate.cfg.xml文件,
        configuration.configure();

        //創建Session工廠對象
        SessionFactory factory = configuration.buildSessionFactory();

        //得到Session對象
        Session session = factory.openSession();

        //使用Hibernate操作數據庫,都要開啟事務,得到事務對象
        Transaction transaction = session.getTransaction();

        //開啟事務
        transaction.begin();

        //把對象添加到數據庫中
        //session.save(user);
        //數據庫操作略

        //提交事務
        transaction.commit();

        //關閉Session
        session.close();

2. mybatis也來了,來的更猛

用的時間稍微長久些,沒辦法,誰讓它勢頭發展的好,也有大廠強烈支持。

mybatis簡介https://baike.baidu.com/item/MyBatis/2824918?fr=aladdin,算是hibernate的競爭者

我看網上也有了類似教程,MyBatis 教程 https://www.w3cschool.cn/mybatis/

重點也是關注架構、緩存和事務

簡單demo如下:

package spring.dao

public interface MybatisDao {
    List<HashMap> selectUser();
}

public class MybatisDaoImpl implements MybatisDao {

    public SqlSession sqlSession;

    public MybatisDaoImpl(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }
    
    @Override
    public List<HashMap> selectUser() {
        // TODO Auto-generated method stub
        return this.sqlSession.selectList("spring.dao.MybatisDao.selectUser");
    }
}

//測試代碼
public class MybatisTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        try {

            System.out.println("開始mybatis實驗");
            MybatisDao userDao;
            SqlSession sqlSession;

            String resource = "conf/mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                    .build(inputStream);
            sqlSession = sqlSessionFactory.openSession();
            userDao = new MybatisDaoImpl(sqlSession);
            List<HashMap> userList = userDao.selectUser();
            for (HashMap user : userList) {
                System.out.println(user);
            }
            sqlSession.close();

            System.out.println("結束mybatis實驗");

        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

配置文件三個如下

 mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties resource="conf/mysql.properties" /> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${mybatis.driver}" /> <property name="url" value="${mybatis.url}" /> <property name="username" value="${mybatis.username}" /> <property name="password" value="${mybatis.password}" /> </dataSource> </environment> </environments> <mappers> <mapper resource="conf/mybatisMapper.xml" /> </mappers> </configuration>

myBatisMapper.xml文件內容

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- mapper:根標簽,namespace:命名空間,一般保證命名空間唯一 --> <mapper namespace="spring.dao.MybatisDao"> <!-- statement,內容:sql語句。id:唯一標識,在同一個命名空間下保持唯一 resultType:sql語句查詢結果集的封裝類型 --> <select id="selectUser" resultType="java.util.Map"> SELECT id, username, number FROM student </select> </mapper>

 數據庫配置mysql.properties

#MySQL for mybatis mybatis.driver=com.mysql.jdbc.Driver mybatis.url=jdbc:mysql://192.168.8.200:3306/bdrackdemo?useUnicode=true&characterEncoding=utf8&autoReconnect=true mybatis.username=root mybatis.password=cstorfs

執行測試代碼效果:

​ 

注意配置型的文件不能放錯位置,要么就改加載屬性文件的代碼,記得導入數據庫驅動包和mybatis.jar

 

緩存分兩種,一級緩存和二級緩存。

一級緩存

 

一級緩存 Mybatis的一級緩存是指SQLSession,一級緩存的作用域是SQlSession, Mabits默認開啟一級緩存。 在同一個SqlSession中,執行相同的SQL查詢時;第一次會去查詢數據庫,並寫在緩存中,第二次會直接從緩存中取。 當執行SQL時候兩次查詢中間發生了增刪改的操作,則SQLSession的緩存會被清空。 每次查詢會先去緩存中找,如果找不到,再去數據庫查詢,然后把結果寫到緩存中。 Mybatis的內部緩存使用一個HashMap,

 public class PerpetualCache implements Cache {

  private final String id;

  //是個map對象
  private final Map<Object, Object> cache = new HashMap<>();

  public PerpetualCache(String id) {
    this.id = id;
  }

  @Override
  public String getId() {
    return id;
  }

  @Override
  public int getSize() {
    return cache.size();
  }

  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }
}

key為hashcode+statementId+sql語句。Value為查詢出來的結果集映射成的java對象。 SqlSession執行insert、update、delete等操作commit后會清空該SQLSession緩存。

 

幾種情況:

第一種情況:同個session進行兩次相同查詢


結論:MyBatis只進行1次數據庫查詢。

 

 第二種情況:同個session進行兩次不同的查詢


結論:MyBatis進行兩次數據庫查詢。

第三種:不同session,進行相同查詢。


結論:MyBatis進行兩次數據庫查詢。

第四種情況:同個session,查詢之后更新數據,再次查詢相同的語句

直接下結論了:更新操作之后緩存會被清除

 小結:一級緩存是SqlSession級別的緩存,Mybatis默認是開啟一級緩存的,當調用SqlSession的修改、添加、刪除、commit()、close()等方法時,就會清空一級緩存。

二級緩存

之所以稱之為“二級緩存”,是相對於“一級緩存”而言的。既然有了一級緩存,那么為什么要提供二級緩存呢?我們知道,在一級緩存中,不同session進行相同SQL查詢的時候,是查詢兩次數據庫的。顯然這是一種浪費,既然SQL查詢相同,就沒有必要再次查庫了,直接利用緩存數據即可,這種思想就是MyBatis二級緩存的初衷。

另外,Spring和MyBatis整合時,每次查詢之后都要進行關閉sqlsession,關閉之后數據被清空。所以MyBatis和Spring整合之后,一級緩存是沒有意義的。如果開啟二級緩存,關閉sqlsession后,會把該sqlsession一級緩存中的數據添加到mapper namespace的二級緩存中。這樣,緩存在sqlsession關閉之后依然存在。

默認情況下,MyBatis只啟用了本地的會話緩存,它僅僅對一個會話中的數據進行緩存,見: MyBatis一級緩存介紹 。要啟用全局的二級緩存,只需要在SQL映射文件中添加一行:

<cache/>

二級緩存是Mapper級別的緩存,多個SqlSession去操作同一個Mapper的sql語句,多個SqlSession可以共用二級緩存,二級緩存是跨SqlSession的。

上面這個簡單語句的效果如下:

映射語句文件中的所有 select 語句的結果將會被緩存。
映射語句文件中的所有 insert、update 和 delete 語句會刷新緩存。
緩存會使用最近最少使用算法(LRU, Least Recently Used)算法來清除不需要的緩存。
緩存不會定時進行刷新(也就是說,沒有刷新間隔)。
緩存會保存列表或對象的1024個引用。
緩存會被視為讀/寫緩存,這意味着獲取到的對象並不是共享的,可以安全地被調用者修改,而不干擾其他調用者或線程所做的潛在修改。

這些屬性可以通過 cache 元素的屬性來修改。比如:

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

這個更高級的配置創建了一個 FIFO 緩存,每隔 60 秒刷新,最多可以存儲結果對象或列表的 512 個引用,而且返回的對象被認為是只讀的,因此對它們進行修改可能會在不同線程中的調用者產生沖突。

可用的清除策略有:

LRU – 最近最少使用:移除最長時間不被使用的對象。
FIFO – 先進先出:按對象進入緩存的順序來移除它們。
SOFT – 軟引用:基於垃圾回收器狀態和軟引用規則移除對象。
WEAK – 弱引用:更積極地基於垃圾收集器狀態和弱引用規則移除對象。
默認的清除策略是 LRU。

flushInterval(刷新間隔)屬性可以被設置為任意的正整數,設置的值應該是一個以毫秒為單位的合理時間量。 默認情況是不設置,也就是沒有刷新間隔,緩存僅僅會在調用語句時刷新。

size(引用數目)屬性可以被設置為任意正整數,要注意欲緩存對象的大小和運行環境中可用的內存資源。默認值是 1024。

readOnly(只讀)屬性可以被設置為 true 或 false。只讀的緩存會給所有調用者返回緩存對象的相同實例。 因此這些對象不能被修改。這就提供了可觀的性能提升。而可讀寫的緩存會(通過序列化)返回緩存對象的拷貝。 速度上會慢一些,但是更安全,因此默認值是 false。

提示:二級緩存是事務性的。這意味着,當 SqlSession 完成並提交時,或是完成並回滾,但沒有執行 flushCache=true 的 insert/delete/update 語句時,緩存會獲得更新。

 小結: 二級緩存是mapper映射級別的緩存,默認是不開啟的,多個SqlSession去操作同一個Mapper映射的sql語句是共用二級緩存的,二級緩存是跨SqlSession的。

框架前幾部分都是屬性文件解析,構造工廠,生產產品做真正的事

記一次使用mybatis使用出現的排序問題: ${}和 #{}的區別

由於開始時排序字段,特別是分頁查詢時,傳的參數有第幾頁,每頁幾條記錄,排序字段,是動態設置的,有時從前端傳來,按某些個字段排序

/**
     * 學生查詢考試任務列表
     * @param user
     * @param pageSize
     * @param pageNumber
     * @param status,試卷狀態(0-N, 1. 未發布,可編輯修改和刪除, 2. 已發布,可查看和停用, 3. 已結束,可審閱, 默認為0代表所有狀態)
     * @param name, 試卷名字模糊查詢
 * @param sortOrder, 排序字段      * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @RequestMapping(value = "/list", method = RequestMethod.GET, produces = "application/json; charset=utf-8")
    @LoginRequired
    @CrossOrigin
    public JSONObject list(@CurrentUser User user,
            @RequestParam(value = "pageSize", required = false, defaultValue = "10")Integer pageSize, 
            @RequestParam(value = "pageNumber", required = false, defaultValue = "1")Integer pageNumber, 
            Integer status, 
            @RequestParam(value = "name", required = false, defaultValue = "") String name,
            @RequestParam(value = "sortOrder", required = false, defaultValue = " cp.id desc ") String sortOrder)

就是這個sortOrder 


 

大致就這樣了,每個人都可以接着擴展開發。框架性迭代總是這樣,xml到json,sql寫在xml文件里到如今通過注解實現(spring也是如此,早期都是xml大量配置,如今也轉換到注解配置),框架越來越包裝了,願每個碼農都把基礎學好,再學框架,不要一上來就mybatis,據說新碼農都沒寫過原生servlet,更別提開發了。。。

總結:

自己早年封裝的jdbc組件,簡單易用,適合小規模化開發,主要當時技術受限,大量數據庫連接沒處理好,沒有池化和緩存策略,且對碼農的sql功底很強,其實面向SQL編程

hibernate,個人覺得一個很不錯得到orm框架,脫離了部分sql,只是沒推廣好

mybatis ,從架構上來說和hibernate雷同,生態圈建立的好,也有大廠光環。

匯總一句話:再好的orm,也脫離不了最最最基本的JDBC,勸扎進框架圈里的人,務必打好基礎,框架各有千秋,沒有最好,只要合適!!!

 

參考:

0. Develop Java applications with Oracle Database   https://www.oracle.com/database/technologies/appdev/jdbc.html

1.  程序員的SQL金典https://github.com/dongguangming/java/blob/master/%E7%A8%8B%E5%BA%8F%E5%91%98%E7%9A%84SQL%E9%87%91%E5%85%B8.pdf

2.  JNDI數據庫連接池配置 https://www.iteye.com/blog/xiaoliang330-978823

3   Hibernate入門這一篇就夠了  

4. Java Mybatis框架入門教程  http://c.biancheng.net/mybatis/

5. JDBC Example – MySQL, Oracle https://www.journaldev.com/2471/jdbc-example-mysql-oracle


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM