前兩天有位學弟問我問題,說他在使用JPA的過程當中遇到一個java類型和mysql類型映射問題,而且在使用TimeStamp 的時候出了錯。咱們這裏從源碼角度宏觀看一下是如何映射的,這裏jpa實現是 hibernatejava
這裏只分享一些重點的,其餘的非重點的跳過mysql
當咱們在jpa中save一個對象的時候,程序毋庸置疑先拿到一個數據庫鏈接,而後把sql語句懟過去。這sql語句是很好實現的,無非就是先判斷主鍵有沒有值,若是有值,就先select一下主鍵的值看是否有數據行,若是有,接下來就是update操做,沒有就是insert操做。若是主鍵爲空,就直接insert了。程序很容易實現後面的insert和update操做的預編譯模板化代碼生成,這裏模板化代碼就像這種 insert into tt (time1, time2, time3, time4) values (?, ?, ?, ?)
,有了模板化sql,接下來就是重點的綁定數據了,如何把java裏的對象轉成sql語句裏的值。有人說直接序列化?對於各類複雜的類型和註解映射,jpa怎麼處理的呢。sql
跳過一堆代理的代碼,咱們看重點數據庫
這裏咱們直接看首次執行的過程,由於我也是在測試用例裏執行,因此一切都得初始化。安全
這裏的作法是獲取實體對象的映射相關描述的實體用於持久化的,這裏先獲取工廠對象獲取到元模型,而後再獲取持久化描述對象多線程
獲取到持久化描述對象,而後進行save操做,併發
這裏展現一下 持久化描述對象的字段高併發
其實這個對象字段很是多,還有不少沒顯示,你們能夠自行參看 EntityPersister
接口,很明顯這個對象記錄了整個映射的信息,包括字段上的註解信息,有了它咱們就能夠進行映射。測試
這裏對每一個字段進行映射hibernate
debug的時候會發現,後面的對象都是泛型的
最終調用doBind,不一樣類型,使用不一樣的對象,咱們這裏debug的時候遇到的是TimeStamp類型對應的對象
好比
AbstractSingleColumnStandardBasicType<T>
實現類是 TimeStampType
BasicBinder<J>
實現類是TimeStampTypeDescriptor
這裏是對 TimeStampTypeDescriptor 對象的獲取 和 調用該對象的bind操做
單例模式
接下來就是重點的清晰的 類型轉換操做啦
到了這裏調用的就不是jpa的邏輯了
PreparedStatement
類是 import java.sql.PreparedStatement
下的 ,到這時候 有可能就有小夥伴問了,爲何調來調去仍是調用了java
的基礎包???
難道仍是基礎包作的轉換???
其實這裏的答案很簡單,確實調了基礎包,可是!調的是基礎包的接口,他的實如今哪呢,這就是傳說中的JDBC
,這裏我使用的是mysql
,因此調用的就是com.mysql.cj.jdbc
下的 ClientPreparedStatement
這就是轉換代碼,由jdbc
實現,因此這就是驅動爲何那麼重要,不一樣數據庫不一樣實現
這裏代碼就很簡單了,不過有沒有發現 轉換隻是把時間戳轉換爲了yyyy-MM-dd HH:mm:ss
形式 插入到數據庫的,其實這裏就是mysql timestamp
存儲的奧祕,你們能夠自行百度一下了解一下。
這裏使用了一個類讓我困惑,每一個學java
的都應該會據說過 SimpleDateFormat
這個類有高併發下多線程安全問題,那爲何,MySQL jdbc
這麼大膽呢?
線程安全解決
這裏互斥變量就是 JdbcConnection
當獲取不到鏈接的時候,線程互斥會報出 Statement.AlreadyClosed
至於後面那個
getConnectionMutex()
調用,這裏說出我我的猜測,找資料沒找到,看了debug內容,猜測是同一個
ClientPreparedQueryBindings
每次只能對應一個JdbcConnection
對象,因此只有共用同一個JdbcConnection
對象時纔會出現線程安全問題,因此這裏互斥變量爲JdbcConnection
,我的看法,你們當心點看
以上純屬我的理解,你們審視着看