歷史就是一面鏡子
回顧自己開發的歷程,見證了時代變遷史記,下面我針對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
2. JNDI數據庫連接池配置 https://www.iteye.com/blog/xiaoliang330-978823
4. Java Mybatis框架入門教程 http://c.biancheng.net/mybatis/