摘要:同時(shí),我們將語(yǔ)句預(yù)編譯在中,這個(gè)類(lèi)可以使用占位符,避免注入,當(dāng)然,后面說(shuō)到的的占位符的原理也是這樣,同時(shí),的占位符原理也是如此。的底層封裝了,比如說(shuō)為了防止注入,一般會(huì)有占位符,也會(huì)有響應(yīng)的占位符。
介紹jdbc
我們學(xué)習(xí)Java數(shù)據(jù)庫(kù)操作時(shí),一般會(huì)設(shè)計(jì)到j(luò)dbc的操作,這是一位程序員最基本的素養(yǎng)。jdbc以其優(yōu)美的代碼和高性能,將瞬時(shí)態(tài)的javabean對(duì)象轉(zhuǎn)化為持久態(tài)的SQL數(shù)據(jù)。但是,每次SQL操作都需要建立和關(guān)閉連接,這勢(shì)必會(huì)消耗大量的資源開(kāi)銷(xiāo);如果我們自行創(chuàng)建連接池,假如每個(gè)項(xiàng)目都這樣做,勢(shì)必搞死的了。同時(shí),我們將SQL語(yǔ)句預(yù)編譯在PreparedStatement中,這個(gè)類(lèi)可以使用占位符,避免SQL注入,當(dāng)然,后面說(shuō)到的hibernate的占位符的原理也是這樣,同時(shí),mybatis的#{}占位符原理也是如此。預(yù)編譯的語(yǔ)句是原生的SQL語(yǔ)句,比如更新語(yǔ)句:
private static int update(Student student) { Connection conn = getConn(); int i = 0; String sql = "update students set Age="" + student.getAge() + "" where Name="" + student.getName() + """; PreparedStatement pstmt; try { pstmt = (PreparedStatement) conn.prepareStatement(sql); i = pstmt.executeUpdate(); System.out.println("resutl: " + i); pstmt.close(); conn.close(); } catch (SQLException e) { e.printStackTrace(); } return i; }
上面的sql語(yǔ)句沒(méi)有使用占位符,如果我們少了varchar類(lèi)型的單引號(hào),就會(huì)保存失敗。在這種情況下,如果寫(xiě)了很多句代碼,最后因?yàn)橐粋€(gè)單引號(hào),導(dǎo)致代碼失敗,對(duì)于程序員來(lái)說(shuō),無(wú)疑是很傷自信心的事。如果涉及到了事務(wù),那么就會(huì)非常的麻煩,一旦一個(gè)原子操作的語(yǔ)句出現(xiàn)錯(cuò)誤,那么事務(wù)就不會(huì)提交,自信心就會(huì)跌倒低谷。然而,這僅僅是更新語(yǔ)句,如果是多表聯(lián)合查詢(xún)語(yǔ)句,那么就要寫(xiě)很多代碼了。具體的jdbc的操作,可以參考這篇文章:jdbc操作。
因而,我們?cè)诳隙ㄋ膬?yōu)點(diǎn)時(shí),也不應(yīng)該規(guī)避他的缺點(diǎn)。隨著工業(yè)化步伐的推進(jìn),每個(gè)數(shù)據(jù)庫(kù)往往涉及到很多表,每張表有可能會(huì)關(guān)聯(lián)到其他表,如果我們還是按照jdbc的方式操作,這無(wú)疑是增加了開(kāi)發(fā)效率。所以,有人厭倦這種復(fù)雜繁瑣、效率低下的操作,于是,寫(xiě)出了著名的hibernate框架,封裝了底層的jdbc操作,以下是jdbc的優(yōu)缺點(diǎn):
由上圖可以看見(jiàn),jdbc不適合公司的開(kāi)發(fā),公司畢竟以最少的開(kāi)發(fā)成本來(lái)創(chuàng)造更多的利益。這就出現(xiàn)了痛點(diǎn),商機(jī)伴隨著痛點(diǎn)的出現(xiàn)。因而,應(yīng)世而生了hibernate這個(gè)框架。即便沒(méi)有hibernate的框架,也會(huì)有其他框架生成。hibernate的底層封裝了jdbc,比如說(shuō)jdbc為了防止sql注入,一般會(huì)有占位符,hibernate也會(huì)有響應(yīng)的占位符。hibernate是orm(object relational mapping)的一種,即對(duì)象關(guān)系映射。
對(duì)象關(guān)系映射通俗地來(lái)說(shuō),對(duì)象在pojo中可以指Javabean,關(guān)系可以指MySQL的關(guān)系型數(shù)據(jù)庫(kù)的表字段與javabean對(duì)象屬性的關(guān)系。映射可以用我們高中所學(xué)的函數(shù)映射,即Javabean瞬時(shí)態(tài)的對(duì)象映射到數(shù)據(jù)庫(kù)的持久態(tài)的數(shù)據(jù)對(duì)象。我們都知道javabean在內(nèi)存中的數(shù)據(jù)是瞬時(shí)狀態(tài)或游離狀態(tài),就像是宇宙星空中的一顆顆行星,除非行星被其他行星所吸引,才有可能不成為游離的行星。瞬時(shí)狀態(tài)(游離狀態(tài))的javabean對(duì)象的生命周期隨著進(jìn)程的關(guān)閉或者方法的結(jié)束而結(jié)束。如果當(dāng)前javabean對(duì)象與gcRoots沒(méi)有直接或間接的關(guān)系,其有可能會(huì)被gc回收。我們就沒(méi)辦法長(zhǎng)久地存儲(chǔ)數(shù)據(jù),這是一個(gè)非常頭疼的問(wèn)題。假如我們使用文件來(lái)存儲(chǔ)數(shù)據(jù),但是文件操作起來(lái)非常麻煩,而且數(shù)據(jù)格式不是很整潔,不利于后期的維護(hù)等。因而,橫空出世了數(shù)據(jù)庫(kù)。我們可以使用數(shù)據(jù)庫(kù)存儲(chǔ)數(shù)據(jù),數(shù)據(jù)庫(kù)中的數(shù)據(jù)才是持久數(shù)據(jù)(數(shù)據(jù)庫(kù)的持久性),除非人為的刪除。這里有個(gè)問(wèn)題——怎么將瞬時(shí)狀態(tài)(游離狀態(tài))的數(shù)據(jù)轉(zhuǎn)化為持久狀態(tài)的數(shù)據(jù),肯定需要一個(gè)連接Javabean和數(shù)據(jù)庫(kù)的橋梁,于是乎就有了上面的jdbc。
多帶帶來(lái)說(shuō)mysql,mysql是一個(gè)遠(yuǎn)程服務(wù)器。我們?cè)谙騧ysql傳輸數(shù)據(jù)時(shí),并不是對(duì)象的方式傳輸,而是以字節(jié)碼的方式傳輸數(shù)據(jù)。為了保證數(shù)據(jù)準(zhǔn)確的傳輸,我們一般會(huì)序列化當(dāng)前對(duì)象,用序列號(hào)標(biāo)志這個(gè)唯一的對(duì)象。如果,我們不想存儲(chǔ)某個(gè)屬性,它是有數(shù)據(jù)庫(kù)中的數(shù)據(jù)拼接而成的,我們大可不用序列化這個(gè)屬性,可以使用Transient來(lái)修飾。比如下面的獲取圖片的路徑,其實(shí)就是服務(wù)器圖片的文件夾地址和圖片的名稱(chēng)拼接而成的。當(dāng)然,你想存儲(chǔ)這個(gè)屬性,也可以存儲(chǔ)這個(gè)屬性。我們有時(shí)候圖片的路由的字節(jié)很長(zhǎng),這樣會(huì)占用MySQL的內(nèi)存。因而,學(xué)會(huì)取舍,未嘗不是一個(gè)明智之舉。
@Entity @Table(name = "core_picture") public class Picture extends BaseTenantObj { 。。。。 @Transient public String getLocaleUrl() { return relativeFolder.endsWith("/") ? relativeFolder + name : relativeFolder + "/" + name; } 。。。。 }
網(wǎng)上流傳盛廣的對(duì)象關(guān)系映射的框架(orm)有hibernate、mybatis等。重點(diǎn)說(shuō)說(shuō)hibernate和mybatis吧。hibernate是墨爾本的一位厭倦重復(fù)的javabean的程序員編寫(xiě)而成的,mybatis是appache旗下的一個(gè)子產(chǎn)品,其都是封裝了jdbc底層操作的orm框架。但hibernate更注重javabean與數(shù)據(jù)表之間的關(guān)系,比如我們可以使用注解生成數(shù)據(jù)表,也可以通過(guò)注解的方式設(shè)置字段的類(lèi)型、注釋等。他將javabean分成了游離態(tài)、瞬時(shí)態(tài)、持久態(tài)等,hibernate根據(jù)這三種狀態(tài)來(lái)觸及javabean對(duì)象的數(shù)據(jù)。而mybatis更多的是注重SQL語(yǔ)句的書(shū)寫(xiě),也就是說(shuō)主要是pojo與SQL語(yǔ)句的數(shù)據(jù)交互,對(duì)此,Hibernate對(duì)查詢(xún)對(duì)象有著良好的管理機(jī)制,用戶(hù)無(wú)需關(guān)心SQL。一旦SQL語(yǔ)句的移動(dòng),有可能會(huì)影響字段的不對(duì)應(yīng),因而,mybatis移植性沒(méi)有hibernate好。mybatis接觸的是底層SQL數(shù)據(jù)的書(shū)寫(xiě),hibernate根據(jù)javabean的參數(shù)來(lái)生成SQL語(yǔ)句,再將SQL查詢(xún)的結(jié)果封裝成pojo,因而,mybatis的性能相來(lái)說(shuō)優(yōu)于hibernate,但這也不是絕對(duì)的。性能還要根據(jù)你的表的設(shè)計(jì)結(jié)構(gòu)、SQL語(yǔ)句的封裝、網(wǎng)絡(luò)、帶寬等等。我只是拋磚引玉,它們具體的區(qū)別,可以參考這篇文檔。mybatis和hibernate的優(yōu)缺點(diǎn)。
hibernate和mybatis之間的區(qū)別,也是很多公司提問(wèn)面試者的問(wèn)題。但是真正熟知他們區(qū)別的人,一般是技術(shù)選型的架構(gòu)師。如果你只是負(fù)責(zé)開(kāi)發(fā),不需要了解它們的區(qū)別,因?yàn)樗麄兌挤庋b了jdbc。所以,你不論使用誰(shuí),都還是比較容易的。然而,很多公司的HR提問(wèn)這種問(wèn)題很死板,HR并不懂技術(shù),他們只是照本宣科的提問(wèn)。如果你照本宣科的回答,它們覺(jué)著你很厲害。但是,如果是一個(gè)懂技術(shù)的人提問(wèn)你,如果你只是臨時(shí)背了它們的區(qū)別,而沒(méi)有相應(yīng)的工作經(jīng)驗(yàn),他們會(huì)問(wèn)的讓你手足無(wú)措。
hibernate講解因?yàn)槲覀児臼褂玫氖莌ibernate,我在這里簡(jiǎn)單地介紹下hibernate。但相對(duì)于jdbc來(lái)說(shuō),hibernate框架還是比較重的。為什么說(shuō)他重,因?yàn)樗闪颂嗟臇|西,看如下的hibernate架構(gòu)圖:
你會(huì)發(fā)現(xiàn)最上層使我們的java應(yīng)用程序的開(kāi)始,比如web的Tomcat服務(wù)器的啟動(dòng),比如main方法的啟動(dòng)等。緊接著就是需要(needing)持久化的對(duì)象,這里為什么說(shuō)是需要,而不是持久化的對(duì)象。只有保存到文件、數(shù)據(jù)庫(kù)中的數(shù)據(jù)才是持久化的。想通過(guò)hibernate,我們可以毫不費(fèi)力的將瞬時(shí)狀態(tài)的數(shù)據(jù)轉(zhuǎn)化為持久狀態(tài)的數(shù)據(jù),下面便是hibernate的內(nèi)部操作數(shù)據(jù)。其一般是這樣的流程:
我個(gè)人畫(huà)的
這個(gè)地址的圖片
如果你是用過(guò)jdbc連接數(shù)據(jù)庫(kù)的話(huà),我們一般是這樣寫(xiě)的:
package com.zby.jdbc.config; import com.zby.util.exception.TableException; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; /** * Created By zby on 22:12 2019/1/5 */ public class InitJdbcFactory { private static Properties properties = new Properties(); private static Logger logger = LoggerFactory.getLogger(InitJdbcFactory.class); static { try { //因?yàn)槭褂玫念?lèi)加載器獲取配置文件,因而,配置文件需要放在classpath下面, // 方能讀到數(shù)據(jù) properties.load(Thread.currentThread().getContextClassLoader(). getResourceAsStream("./jdbc.properties")); } catch (IOException e) { logger.info("初始化jdbc失敗"); e.printStackTrace(); } } public static Connection createConnection() { String drivers = properties.getProperty("jdbc.driver"); if (StringUtils.isBlank(drivers)) { drivers = "com.mysql.jdbc.Driver"; } String url = properties.getProperty("jdbc.url"); String username = properties.getProperty("jdbc.username"); String password = properties.getProperty("jdbc.password"); try { Class.forName(drivers); return DriverManager.getConnection(url, username, password); } catch (ClassNotFoundException e) { logger.error(InitColTable.class.getName() + ":連接數(shù)據(jù)庫(kù)的找不到驅(qū)動(dòng)類(lèi)"); throw new TableException(InitColTable.class.getName() + ": 連接數(shù)據(jù)庫(kù)的找不到驅(qū)動(dòng)類(lèi)", e); } catch (SQLException e) { logger.error(InitColTable.class.getName() + ":連接數(shù)據(jù)庫(kù)的sql異常"); throw new TableException(InitColTable.class.getName() + "連接數(shù)據(jù)庫(kù)的sql異常", e); } } }
hibernate一般這樣連接數(shù)據(jù)庫(kù):
public class HibernateUtils { private static SessionFactory sf; //靜態(tài)初始化 static{ //【1】加載配置文件 Configuration conf = new Configuration().configure(); //【2】 根據(jù)Configuration 配置信息創(chuàng)建 SessionFactory sf = conf.buildSessionFactory(); //如果這里使用了hook虛擬機(jī),需要關(guān)閉hook虛擬機(jī) Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { System.out.println("虛擬機(jī)關(guān)閉!釋放資源"); sf.close(); } })); } /** * 采用openSession創(chuàng)建一個(gè)與數(shù)據(jù)庫(kù)的連接會(huì)話(huà),但這種方式需要手動(dòng)關(guān)閉與數(shù)據(jù)庫(kù)的session連接(會(huì)話(huà)), * 如果不關(guān)閉,則當(dāng)前session占用數(shù)據(jù)庫(kù)的資源無(wú)法釋放,最后導(dǎo)致系統(tǒng)崩潰。 * **/ public static org.hibernate.Session openSession(){ //【3】 獲得session Session session = sf.openSession(); return session; } /** * 這種方式連接數(shù)據(jù)庫(kù),當(dāng)提交事務(wù)時(shí),會(huì)自動(dòng)關(guān)閉當(dāng)前會(huì)話(huà); * 同時(shí),創(chuàng)建session連接時(shí),autoCloseSessionEnabled和flushBeforeCompletionEnabled都為true, * 并且session會(huì)同sessionFactory組成一個(gè)map以sessionFactory為主鍵綁定到當(dāng)前線(xiàn)程。 * 采用getCurrentSession()需要在Hibernate.cfg.xml配置文件中加入如下配置: 如果是本地事務(wù),及JDBC一個(gè)數(shù)據(jù)庫(kù):thread 如果是全局事務(wù),及jta事務(wù)、多個(gè)數(shù)據(jù)庫(kù)資源或事務(wù)資源:jta 使用spring的getHiberanteTemplate 就不需要考慮事務(wù)管理和session關(guān)閉的問(wèn)題: * **/ public static org.hibernate.Session getCurrentSession(){ //【3】 獲得session Session session = sf.getCurrentSession(); return session; } }
mybatis的配置文件:
public class DBTools { public static SqlSessionFactory sessionFactory; static{ try { //使用MyBatis提供的Resources類(lèi)加載mybatis的配置文件 Reader reader = Resources.getResourceAsReader("mybatis.cfg.xml"); //構(gòu)建sqlSession的工廠(chǎng) sessionFactory = new SqlSessionFactoryBuilder().build(reader); } catch (Exception e) { e.printStackTrace(); } } //創(chuàng)建能執(zhí)行映射文件中sql的sqlSession public static SqlSession getSession(){ return sessionFactory.openSession(); } }
hibernate、mybatis、jdbc創(chuàng)建于數(shù)據(jù)庫(kù)的連接方式雖然不同,但最紅都是為了將瞬時(shí)態(tài)的數(shù)據(jù)寫(xiě)入到數(shù)據(jù)庫(kù)中的,但這里主要說(shuō)的是hibernate。但是hibernate已經(jīng)封裝了這些屬性,我們可以在configuration在配置驅(qū)動(dòng)類(lèi)、用戶(hù)名、用戶(hù)密碼等。再通過(guò)sessionFactory創(chuàng)建session會(huì)話(huà),也就是加載Connection的物理連接,創(chuàng)建sql的事務(wù),然后執(zhí)行一系列的事務(wù)操作,如果事務(wù)全部成功即可成功,但凡有一個(gè)失敗都會(huì)失敗。jdbc是最基礎(chǔ)的操作,但是,萬(wàn)丈高樓平地起,只有基礎(chǔ)打牢,才能走的更遠(yuǎn)。因?yàn)閔ibernate封裝了這些基礎(chǔ),我們操作數(shù)據(jù)庫(kù)不用考慮底層如何實(shí)現(xiàn)的,因而,從某種程度上來(lái)說(shuō),hibernate還是比較重的。
hibernate為什么重(zhong)?比如我們執(zhí)行插入語(yǔ)句,可以使用save、saveOrUpdate,merge等方法。需要將實(shí)體bean通過(guò)反射轉(zhuǎn)化為mysql的識(shí)別的SQL語(yǔ)句,同時(shí),查詢(xún)雖然用到了反射,但是最后轉(zhuǎn)化出來(lái)的還是object的根對(duì)象,這時(shí)需要將根對(duì)象轉(zhuǎn)化為當(dāng)前對(duì)象,返回給客戶(hù)端。雖然很笨重,但是文件配置好了,可以大大地提高開(kāi)發(fā)效率。畢竟現(xiàn)在的服務(wù)器的性能都比較好,公司追求的是高效率的開(kāi)發(fā),而往往不那么看重性能,除非用戶(hù)提出性能的問(wèn)題。
merge和saveOrUpdatemerge方法與saveOrUpdate從功能上類(lèi)似,但他們?nèi)杂袇^(qū)別。現(xiàn)在有這樣一種情況:我們先通過(guò)session的get方法得到一個(gè)對(duì)象u,然后關(guān)掉session,再打開(kāi)一個(gè)session并執(zhí)行saveOrUpdate(u)。此時(shí)我們可以看到拋出異常:Exception in thread "main" org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session,即在session緩存中不允許有兩個(gè)id相同的對(duì)象。不過(guò)若使用merge方法則不會(huì)異常,其實(shí)從merge的中文意思(合并)我們就可以理解了。我們重點(diǎn)說(shuō)說(shuō)merge。merge方法產(chǎn)生的效果和saveOrUpdate方法相似。這是hibernate的原碼:
void saveOrUpdate(Object var1); void saveOrUpdate(String var1, Object var2); public Object merge(Object object); public Object merge(String var1, Object var2);
前者不用返回任何數(shù)據(jù),后者返回的是持久化的對(duì)象。如果根據(jù)hibernate的三種狀態(tài),比如瞬時(shí)態(tài)、持久態(tài)、游離態(tài)來(lái)說(shuō)明這個(gè)問(wèn)題,會(huì)比較難以理解,現(xiàn)在,根據(jù)參數(shù)有無(wú)id或id是否已經(jīng)存在來(lái)理解merge,而且從執(zhí)行他們兩個(gè)方法而產(chǎn)生的sql語(yǔ)句來(lái)看是一樣的。
參數(shù)實(shí)例對(duì)象沒(méi)有提供id或提供的id在數(shù)據(jù)庫(kù)找不到對(duì)應(yīng)的行數(shù)據(jù),這時(shí)merger將執(zhí)行插入操作嗎,產(chǎn)的SQL語(yǔ)句如下:
Hibernate: select max(uid) from user Hibernate: insert into hibernate1.user (name, age, uid) values (?, ?, ?)
一般情況下,我們新new一個(gè)對(duì)象,或者從前端向后端傳輸javabean序列化的對(duì)象時(shí),都不會(huì)存在當(dāng)前對(duì)象的id,如果使用merge的話(huà),就會(huì)向數(shù)據(jù)庫(kù)中插入一條數(shù)據(jù)。
參數(shù)實(shí)例對(duì)象的id在數(shù)據(jù)庫(kù)中已經(jīng)存在,此時(shí)又有兩種情況
(1)如果對(duì)象有改動(dòng),則執(zhí)行更新操作,產(chǎn)生sql語(yǔ)句有:
Hibernate: select user0_.uid as uid0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from hibernate1.user user0_ where user0_.uid=? Hibernate: update hibernate1.user set name=?, age=? where uid=?
(2)如果對(duì)象未改動(dòng),則執(zhí)行查詢(xún)操作,產(chǎn)生的語(yǔ)句有:
Hibernate: select user0_.uid as uid0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from hibernate1.user user0_ where user0_.uid=?
以上三種是什么情況呢?如果我們保存用戶(hù)時(shí),數(shù)據(jù)庫(kù)中肯定不存在即將添加的用戶(hù),也就是說(shuō),我們的保存用戶(hù)就是向數(shù)據(jù)庫(kù)中添加用戶(hù)。但是,其也會(huì)跟著某些屬性, 比如說(shuō)用戶(hù)需要頭像,這是多對(duì)一的關(guān)系,一個(gè)用戶(hù)可能多個(gè)對(duì)象,然而,頭像的關(guān)聯(lián)的id不是放在用戶(hù)表中的,而是放在用戶(hù)擴(kuò)張表中的,這便用到了切分表的概念。題外話(huà),我們有時(shí)會(huì)用到快照表,比如商品快照等,也許,我們購(gòu)買(mǎi)商品時(shí),商品是一個(gè)價(jià)格,但隨后商品的價(jià)格變了,我們需要退商品時(shí),就不應(yīng)該用到商品改變后的價(jià)格了,而是商品改變前的價(jià)格。擴(kuò)展表存放用戶(hù)額外的信息,也就是用戶(hù)非必須的信息,比如說(shuō)昵稱(chēng),性別,真實(shí)姓名,頭像等。 因而,頭像是圖片類(lèi)型,使用hibernate的注解方式,創(chuàng)建用戶(hù)表、圖片表、用戶(hù)擴(kuò)展表。如下所示(部分重要信息已省略)
//用戶(hù)頭像 @Entity @Table(name = "core_user") public class User extends BaseTenantConfObj { /** * 擴(kuò)展表 * */ @OneToOne(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.ALL) private UserExt userExt; } //用戶(hù)擴(kuò)展表的頭像屬性 @Entity @Table(name = "core_user_ext") public class UserExt implements Serializable { /** * 頭像 */ @ManyToOne @JoinColumn(name = "head_logo") private Picture headLogo; } //圖片表 @Entity @Table(name = "core_picture") public class Picture extends BaseTenantObj { private static Logger logger = LoggerFactory.getLogger(Picture.class); 。。。。。。 //圖片存放在第三方的相對(duì)url。 @Column(name = "remote_relative_url", length = 300) private String remoteRelativeUrl; // 圖片大小 @Column(length = 8) private Integer size; /** * 圖片所屬類(lèi)型 * user_logo:用戶(hù)頭像 */ @Column(name = "host_type", length = 58) private String hostType; //照片描述 @Column(name = "description", length = 255) private String description; }
前端代碼是:
//這里使用到了vue.js的代碼,v-model數(shù)據(jù)的雙向綁定,前端的HTML代碼//這里用到了js代碼,這里用到了js的屬性方法 upImg: function(me) { Utils.asyncImg({ fn: function(data) { vm.pageData.userInfo.logo = { path: data.remoteRelativeUrl, id: data.id }; } }); }, 頭像: ![]()
推薦尺寸800*800;支持.jpg, .jpeg, .bmp, .png類(lèi)型文件,1M以?xún)?nèi)
上傳頭像是異步提交,如果用戶(hù)上傳了頭像,我們?cè)谔峤挥脩?hù)信息時(shí),通過(guò)“headLogo.id”可以獲取當(dāng)前頭像的持久化的圖片對(duì)象,hibernate首先會(huì)根據(jù)屬性headLogo找到圖片表,根據(jù)當(dāng)前頭像的id找到圖片表中對(duì)應(yīng)的行數(shù)據(jù),為什么可以根據(jù)id來(lái)獲取行數(shù)據(jù)?
-圖片表的表結(jié)構(gòu)信息
從這張圖片可以看出,圖片采用默認(rèn)的存儲(chǔ)引擎,也就是InnoDB存儲(chǔ)引擎,而不是myiSam的存儲(chǔ)引擎。
innodb存儲(chǔ)引擎我們都知道這兩種存儲(chǔ)引擎的區(qū)別,如果不知道的話(huà),可以參這篇文章MySQL中MyISAM和InnoDB的索引方式以及區(qū)別與選擇。innodb采用BTREE樹(shù)的數(shù)據(jù)結(jié)構(gòu)方式存儲(chǔ),它有0到1直接前繼和0到n個(gè)直接后繼,這是什么意思呢?一棵樹(shù)當(dāng)前葉子節(jié)點(diǎn)的直接父節(jié)點(diǎn)只有一個(gè),但其兒子節(jié)點(diǎn)可以一個(gè)都沒(méi)有,也可以有1個(gè)、2個(gè)、3個(gè)......,如果mysql采用的多對(duì)一的方式存儲(chǔ)的話(huà),你就會(huì)發(fā)現(xiàn)某條外鍵下有許多行數(shù)據(jù),比如如下的這張表
這張表記錄的是項(xiàng)目的完成情況,一般有預(yù)約階段,合同已簽,合同完成等等。你會(huì)發(fā)現(xiàn)project_id=163的行數(shù)據(jù)不止一條,我們通過(guò)查詢(xún)語(yǔ)句:SELECT zpp.* from zq_project_process zpp WHERE zpp.is_deleted = 0 AND zpp.project_id=163,查找速度非常快。為什么這么快呢,因?yàn)槲覄傞_(kāi)始說(shuō)的innodb采用的BTREE樹(shù)結(jié)構(gòu)存儲(chǔ),其數(shù)據(jù)是放在當(dāng)前索引下,什么意思?innodb的存儲(chǔ)引擎是以索引作為當(dāng)前節(jié)點(diǎn)值,比如說(shuō)銀行卡表的有個(gè)主鍵索引,備注,如果我們沒(méi)有創(chuàng)建任何索引,如果采用的innodb的數(shù)據(jù)引擎,其內(nèi)部會(huì)創(chuàng)建一個(gè)默認(rèn)的行索引,這就像我們?cè)趧?chuàng)建javabean對(duì)象時(shí),沒(méi)有創(chuàng)建構(gòu)造器,其內(nèi)部會(huì)自動(dòng)創(chuàng)建一個(gè)構(gòu)造器的道理是一樣的。其數(shù)據(jù)是怎么存儲(chǔ)的呢,如下圖所示:
mysql銀行卡數(shù)據(jù)
其內(nèi)部存儲(chǔ)數(shù)據(jù)
其所對(duì)應(yīng)的行數(shù)據(jù)是放在當(dāng)前索引下的,因而,我們?nèi)?shù)據(jù)不是取表中的數(shù)據(jù),而是取當(dāng)前主鍵索引下的數(shù)據(jù)。項(xiàng)目進(jìn)程表如同銀行卡的主鍵索引,只不過(guò)其有三個(gè)索引,分別是主鍵索引和兩個(gè)外鍵索引,如圖所示的索引:
索引名是hibernate自動(dòng)生成的一個(gè)名字,索引是項(xiàng)目id、類(lèi)型兩個(gè)索引。因?yàn)槲覀儾皇菑谋碇腥?shù)據(jù),而是從當(dāng)前索引的節(jié)點(diǎn)下取數(shù)據(jù),所以速度當(dāng)然快了。索引有主鍵索引、外鍵索引、聯(lián)合索引等,但一般情況下,主鍵索引和外鍵索引使用頻率比較高。同時(shí),innodb存儲(chǔ)引擎的支持事務(wù)操作,這是非常重要,我們操作數(shù)據(jù)庫(kù),一般都是設(shè)計(jì)事務(wù)的操作,這也mysql默認(rèn)的存儲(chǔ)引擎是innodb。
我們通過(guò)主鍵獲取圖片的行數(shù)據(jù),就像通過(guò)主鍵獲取銀行卡的行數(shù)據(jù)。這也是上面所說(shuō)的,根據(jù)是否有id來(lái)確定是插入還是更新數(shù)據(jù)。通過(guò)圖片主鍵id獲取該行數(shù)據(jù)后,hibernate會(huì)在堆中創(chuàng)建一個(gè)picture對(duì)象。用戶(hù)擴(kuò)展表的headLogo屬性指向這個(gè)圖片對(duì)象的首地址,從而創(chuàng)建一個(gè)持久化的圖片對(duì)象。前臺(tái)異步提交頭像時(shí),如果是編輯頭像,hibernate會(huì)覺(jué)擦到當(dāng)前對(duì)象的屬性發(fā)生了改變,于是,在提交事務(wù)時(shí)將修改后的游離態(tài)的類(lèi)保存到數(shù)據(jù)庫(kù)中。如果我們保存或修改用戶(hù)時(shí),我們保存的就是持久化的對(duì)象,其內(nèi)部會(huì)自動(dòng)存儲(chǔ)持久化頭像的id。這是hibernate底層所做的,我們不需要關(guān)心。
再舉一個(gè)hibernate事務(wù)提交的例子:
我們?cè)谥Ц懂?dāng)中搞得提現(xiàn)事務(wù)時(shí),調(diào)用第三方支付的SDK時(shí),第三方一般會(huì)用我們到訂單號(hào),比如我們調(diào)用連連支付這個(gè)第三方支付的SDK的payRequestBean的實(shí)體類(lèi):
/** * Created By zby on 11:00 2018/12/11 * 發(fā)送到連連支付的body內(nèi)容 */ @Data @AllArgsConstructor @NoArgsConstructor public class PaymentRequestBean extends BaseRequestBean { /** * 版本號(hào) */ @NonNull private String api_version; /** * 銀行賬戶(hù) */ @NonNull private String card_no; /** * 對(duì)私 */ @NonNull private String flag_card; /** * 回調(diào)接口 */ @NonNull private String notify_url; /** * 商戶(hù)訂單號(hào) */ @NonNull private String no_order; /** * 商戶(hù)訂單時(shí)間,時(shí)間格式為 YYYYMMddHHmmss */ @NonNull private String dt_order; /** * 交易金額 */ @NonNull public String money_order; /** * 收款方姓名 即賬戶(hù)名 */ @NonNull private String acct_name; /** * 收款銀行姓名 */ private String bank_name; /** * 訂單描述 ,代幣類(lèi)型 + 支付 */ @NonNull private String info_order; /** * 收款備注 */ private String memo; /** * 支行名稱(chēng) */ private String brabank_name; }
商戶(hù)訂單號(hào)是必傳的,且這個(gè)訂單號(hào)是我們這邊提供的,這就有一個(gè)問(wèn)題了,怎么避免訂單號(hào)不重復(fù)呢?我們可以在提現(xiàn)記錄表事先存儲(chǔ)一個(gè)訂單號(hào),訂單號(hào)的規(guī)則如下:"WD" +系統(tǒng)時(shí)間+ 當(dāng)前提現(xiàn)記錄的id,這個(gè)id怎么拿到呢?既然底層使用的是merge方法,我們事先不創(chuàng)建訂單號(hào),先保存這個(gè)記錄,其返回的是已經(jīng)創(chuàng)建好的持久化的對(duì)象,該持久化的對(duì)象肯定有提現(xiàn)主鍵的id。我們拿到該持久化對(duì)象的主鍵id,便可以封裝訂單號(hào),再次保存這個(gè)持久化的對(duì)象,其內(nèi)部會(huì)執(zhí)行類(lèi)似以下的操作:
Hibernate: select user0_.uid as uid0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from hibernate1.user user0_ where user0_.uid=? Hibernate: update hibernate1.user set name=?, age=? where uid=?
代碼如下:
withdraw.setWithdrawStatus(WITHDRAW_STATUS_WAIT_PAY); withdraw.setApplyTime(currentTime); withdraw.setExchangeHasThisMember(hasThisMember ? YES : NO); withdraw = withdrawDao.save(withdraw); withdraw.setOrderNo("WD" + DateUtil.ISO_DATETIME_FORMAT_NONE.format(currentTime) + withdraw.getId()); withdrawDao.save(withdraw);
不管哪種情況,merge的返回值都是一個(gè)持久化的實(shí)例對(duì)象,但對(duì)于參數(shù)而言不會(huì)改變它的狀態(tài)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/73170.html
摘要:一配置屬性詳解可以在各式各樣不同環(huán)境下工作而設(shè)計(jì)的因此存在著大量的配置參數(shù)。以簡(jiǎn)便操作,多數(shù)配置參數(shù)都有默認(rèn)的配置值也是我們?nèi)粘J褂玫谋仨毱贰? Hibernate (開(kāi)放源代碼的對(duì)象關(guān)系映射框架) Hibernate是一個(gè)開(kāi)放源代碼的對(duì)象關(guān)系映射框架,它對(duì)JDBC進(jìn)行了非常輕量級(jí)的對(duì)象封裝, 它將POJO與數(shù)據(jù)庫(kù)表建立映射關(guān)系,是一個(gè)全自動(dòng)的orm框架,hibernat...
摘要:不管是還是,表之間的連接查詢(xún),被映射為實(shí)體類(lèi)之間的關(guān)聯(lián)關(guān)系,這樣,如果兩個(gè)實(shí)體類(lèi)之間沒(méi)有實(shí)現(xiàn)關(guān)聯(lián)關(guān)系,你就不能把兩個(gè)實(shí)體或者表起來(lái)查詢(xún)。 因?yàn)轫?xiàng)目需要選擇數(shù)據(jù)持久化框架,看了一下主要幾個(gè)流行的和不流行的框架,對(duì)于復(fù)雜業(yè)務(wù)系統(tǒng),最終的結(jié)論是,JOOQ是總體上最好的,可惜不是完全免費(fèi),最終選擇JDBC Template。 Hibernate和Mybatis是使用最多的兩個(gè)主流框架,而JOO...
摘要:在使用作為應(yīng)用時(shí)推薦使用作為開(kāi)發(fā)工具導(dǎo)入相應(yīng)的的包到文件下的目錄下關(guān)于開(kāi)發(fā)中導(dǎo)入的說(shuō)明在此提供一個(gè)包下載鏈接,地址百度云盤(pán)下載好以后解壓到某個(gè)文件夾里解壓好以后,開(kāi)發(fā)所需要的包在解壓后的包下,將該文件夾下的包復(fù)制到項(xiàng)目中另外還需 1.在使用Hibernate作為orm應(yīng)用時(shí)推薦使用myeclipse作為開(kāi)發(fā)工具2.導(dǎo)入相應(yīng)的Hibernate的jar包到webroot文件下的lib目錄...
閱讀 1397·2021-11-25 09:43
閱讀 818·2021-11-18 10:02
閱讀 3026·2021-09-07 09:59
閱讀 2821·2021-08-30 09:44
閱讀 2976·2019-08-30 13:17
閱讀 2374·2019-08-29 12:17
閱讀 1732·2019-08-28 17:57
閱讀 1344·2019-08-26 14:04