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

查詢結果

恩早期開發就是這麼過來,只是自己封裝成了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

 

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

一級緩存的幾種情況:

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

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

 

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

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

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

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

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

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

 

二級緩存

之所以稱之爲“二級緩存”,是相對於“一級緩存”而言的。既然有了一級緩存,那麼爲什麼要提供二級緩存呢?我們知道,在一級緩存中,不同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 語句時,緩存會獲得更新。

 

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

記一次使用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/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章