摘要:導(dǎo)讀源碼地址公司的持久層采用的框架,這也是很多公司使用的一種持久層框架。配置文件本項目以為開發(fā)環(huán)境和以搭建的,分為包和包。一種是,這種是手動提交事務(wù)。返回結(jié)果分為兩種,一種是以實體類直接返回,調(diào)用方法。
導(dǎo)讀
源碼地址
公司的持久層采用的hibernate框架,這也是很多公司使用的一種持久層框架。它將瞬時態(tài)的javabean對象轉(zhuǎn)化為持久態(tài)數(shù)據(jù)表的字段對象、或?qū)⒊志脩B(tài)的字段對象轉(zhuǎn)化為瞬時態(tài)javabean對象。我比較喜歡看源碼,看別人的架構(gòu)思想,因為,筆者想向架構(gòu)師的方向進(jìn)發(fā)。看了別人的源碼,突然想模擬hibernate框架,自己寫個框架出來。 這里去除了hibernate框架晦澀的地方,當(dāng)做自己學(xué)習(xí)材料還是不錯的。里面涉及到反射、連接池等等。 這個項目中,你可以知道數(shù)據(jù)庫連接池是怎么建的,又是怎么回收的。 使用靜態(tài)代碼塊加載配置文件
以下詳細(xì)介紹我個人的項目,但肯定沒有人家源碼寫得好,這里僅作為學(xué)習(xí)使用。
如果不懂的,可以私信我。
配置文件本項目以idea為開發(fā)環(huán)境和以maven搭建的,分為java包和test包。java包的配置文件放在resources下,代碼放在com.zby.simulationHibernate包下,如下是配置文件:
連接池我們在使用hibernate時,一般會配置連接池,比如,初始化連接數(shù)是多少,最大連接數(shù)是多少?這個連接的是什么?我們在啟動項目時,hibernate根據(jù)初始的連接數(shù),來創(chuàng)建多少個數(shù)據(jù)庫連接對象,也就是jdbc中的Connection對象。
為什么要有這個連接池?因為,每次開啟一個連接和關(guān)閉一個連接都是消耗資源的,我們開啟了這些連接對象之后,把它們放在一個容器中,我們何時需要何時從容器中取出來。當(dāng)不需要的時候,再將踏進(jìn)放回到容器中。因而,可以減少占用的資源。
如下,是初始化的連接對象:
package com.zby.simulationHibernate.util.factory; import com.zby.simulationHibernate.util.exception.GenericException; import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.io.InputStream; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; /** * Created By zby on 21:23 2019/1/23 * 數(shù)據(jù)庫的連接 */ public class Connect { /** * 連接池的初始值 */ private static int initPoolSize = 20; /** * 創(chuàng)建property的配置文件 */ protected static Properties properties; /** * 連接池的最小值 */ protected static int minPoolSize; /** * 連接池的最大值 */ protected static int maxPoolSize; //【2】靜態(tài)代碼塊 static { //加載配置文件 properties = new Properties(); InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("db.properties"); try { properties.load(is); minPoolSize = Integer.valueOf(properties.getProperty("jdbc.minConnPool")); if (minPoolSize <= initPoolSize) minPoolSize = initPoolSize; maxPoolSize = Integer.valueOf(properties.getProperty("jdbc.maxConnPool")); if (minPoolSize > maxPoolSize) throw new GenericException("連接池的最小連接數(shù)不能大于最大連接數(shù)"); } catch (IOException e) { System.out.println("未找到配置文件"); e.printStackTrace(); } } /** * Created By zby on 16:50 2019/1/23 * 獲取數(shù)據(jù)連接 */ protected java.sql.Connection createConnect() { String driverName = properties.getProperty("jdbc.driver"); if (StringUtils.isEmpty(driverName)) { driverName = "com.mysql.jdbc.Driver"; } String userName = properties.getProperty("jdbc.username"); String password = properties.getProperty("jdbc.password"); String dbUrl = properties.getProperty("jdbc.url"); try { Class.forName(driverName); return DriverManager.getConnection(dbUrl, userName, password); } catch (ClassNotFoundException e) { System.out.println("找不到驅(qū)動類"); e.printStackTrace(); } catch (SQLException e) { System.out.println("加載異常"); e.printStackTrace(); } return null; } }創(chuàng)建Session會話
我們在使用hibernate時,不是直接使用連接對象,而是,以會話的方式創(chuàng)建一個連接。創(chuàng)建會話的方式有兩種。一種是openSession,這種是手動提交事務(wù)。getCurrentSession是自動提交事務(wù)。
如代碼所示:
package com.zby.simulationHibernate.util.factory; import java.sql.Connection; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * Created By zby on 15:43 2019/1/23 */ public class SqlSessionFactory implements SessionFactory { /** * 連接池 */ private static List數(shù)據(jù)查找connections; /** * 連接對象 * * @return */ private static Connect connect = new Connect(); protected static List getConnections() { return connections; } //靜態(tài)代碼塊,初始化常量池 static { connections = new ArrayList<>(); Connection connection; for (int i = 0; i < Connect.minPoolSize; i++) { connection = connect.createConnect(); connections.add(connection); } } @Override public Session openSession() { return getSession(false); } @Override public Session getCurrentSession() { return getSession(true); } /** * 獲取session * * @param autoCommit 是否自動提交事務(wù) * @return */ private Session getSession(boolean autoCommit) { //【1】判斷連接池有可用的連接對象 boolean hasNoValidConn = hasValidConnction(); //【2】沒有可用的連接池,使用最大的連接池 if (!hasNoValidConn) { for (int i = 0; i < (Connect.maxPoolSize - Connect.minPoolSize); i++) { connections.add(connect.createConnect()); } } //【3】有可用的連接 for (Iterator iterator = connections.iterator(); iterator.hasNext(); ) { Connection connection = null; try { connection = (Connection) iterator.next(); connection.setAutoCommit(autoCommit); Session session = new Session(connection); iterator.remove(); return session; } catch (Exception e) { e.printStackTrace(); } } return null; } /** * Created By zby on 21:50 2019/1/23 * 當(dāng)我們沒開啟一個連接,連接池的數(shù)目減少1,直到連接池的數(shù)量為0 */ private boolean hasValidConnction() { return null != connections && connections.size() != 0; } }
我們既然使用這個框架,必然要有數(shù)據(jù)查找的功能。返回結(jié)果分為兩種,一種是以實體類直接返回,調(diào)用addEntity方法。但是,多數(shù)情況下,是多張表聯(lián)合查詢的結(jié)果,這種情況下,直接以實體類接肯定不可以的。因而,我們需要自定義接收對象,并將查找結(jié)果進(jìn)行過濾,再封裝成我們想要的對象。
第一種,以實體類返回
/** * Created By zby on 23:19 2019/1/23 * 體檢反射的實體類 */ public SqlQuery addEntity(ClasspersistenceClass) { this.persistenceClass = persistenceClass; return this; }
第二種,過濾后返回數(shù)據(jù)
/** * Created By zby on 19:18 2019/1/27 * 創(chuàng)建類型 */ public SqlQuery addScalar(String tuple, String alias) { if (CommonUtil.isNull(aliasMap)) { aliasMap = new HashMap<>(); } for (Map.Entryentry : aliasMap.entrySet()) { String key = entry.getKey(); if (key.equals(tuple)) throw new GenericException("alias已經(jīng)存在,即alias=" + key); String value = aliasMap.get(key); if (value.equals(alias) && key.equals(tuple)) throw new GenericException("當(dāng)前alias的type已經(jīng)存在,alias=" + key + ",type=" + value); } aliasMap.put(tuple, alias); return this; } /** * Created By zby on 9:20 2019/1/28 * 數(shù)據(jù)轉(zhuǎn)換問題 */ public SqlQuery setTransformer(ResultTransformer transformer) { if (CommonUtil.isNull(aliasMap)) { throw new IllegalArgumentException("請?zhí)砑愚D(zhuǎn)換的屬性數(shù)量"); } transformer.transformTuple(aliasMap); this.transformer = transformer; return this; }
以集合的方式返回數(shù)據(jù):
/** * Created By zby on 17:02 2019/1/29 * 設(shè)置查找參數(shù) */ public SqlQuery setParamter(int start, Object param) { if (CommonUtil.isNull(columnParamer)) columnParamer = new HashMap<>(); columnParamer.put(start, param); return this; } /** * Created By zby on 16:41 2019/1/24 * 查找值 */ public Listlist() { PreparedStatement statement = null; ResultSet resultSet = null; try { statement = connection.prepareStatement(sql); if (CommonUtil.isNotNull(columnParamer)) { for (Map.Entry entry : columnParamer.entrySet()) { int key = entry.getKey(); Object value = entry.getValue(); statement.setObject(key + 1, value); } } resultSet = statement.executeQuery(); PersistentObject persistentObject = new PersistentObject(persistenceClass, resultSet); if (CommonUtil.isNotNull(aliasMap)) return persistentObject.getPersist(transformer); return persistentObject.getPersist(); } catch (Exception e) { e.printStackTrace(); } finally { SessionClose.closeConnStateResSet(connection, statement, resultSet); } return null; }
返回唯一值
/** * Created By zby on 16:41 2019/1/24 * 查找值 */ public T uniqueResult() { Listlist = list(); if (CommonUtil.isNull(list)) return null; if (list.size() > 1) throw new GenericException("本來需要返回一個對象,卻返回 " + list.size() + "個對象"); return list.get(0); }
測試
@Test public void testList() { Session session = new SqlSessionFactory().openSession(); String sql = "SELECT " + " customer_name AS customerName, " + " `name` AS projectName " + "FROM " + " project where id >= ? and id <= ?"; SqlQuery query = session.createSqlQuery(sql); query.setParamter(0, 1); query.setParamter(1, 2); query.addScalar("customerName", StandardBasicTypes.STRING) .addScalar("projectName", StandardBasicTypes.STRING); query.setTransformer(Transforms.aliasToBean(ProjectData.class)); List保存數(shù)據(jù)projects = query.list(); for (ProjectData project : projects) { System.out.println(project.getCustomerName() + " " + project.getProjectName()); } } @Ignore public void testListNoData() { Session session = new SqlSessionFactory().openSession(); String sql = "SELECT " + " customer_name AS customerName, " + " `name` AS projectName " + "FROM " + " project where id >= ? and id <= ?"; SqlQuery query = session.createSqlQuery(sql). setParamter(0, 1). setParamter(1, 2). addEntity(Project.class); List projects = query.list(); for (Project project : projects) { System.out.println(project.getCustomerName() + " " + project.getGuestCost()); } }
我們這里以 merge 方法來保存數(shù)據(jù),因為這個方法非常的特殊,我們在這里做特殊說明。如果該瞬時態(tài)的對象有主鍵,而且,其在數(shù)據(jù)表中已經(jīng)存在該主鍵的字段對象,我們此時就更新該數(shù)據(jù)表。如果數(shù)據(jù)表中沒有當(dāng)前主鍵的字段對象,我們向數(shù)據(jù)庫中添加該對象的值。如果該瞬時態(tài)的對象沒有主鍵,我們直接在數(shù)據(jù)表中添加該對象。
如代碼所示:
/** * Created By zby on 15:41 2019/1/29 * 合并,首先判斷id是否存在,若id存在則更新,若id不存在,則保存數(shù)據(jù) */ public T merge(T t) { if (CommonUtil.isNull(t)) throw new IllegalArgumentException("參數(shù)為空"); Classclazz = (Class ) t.getClass(); Field[] fields = clazz.getDeclaredFields(); boolean isContainsId = CommonUtil.isNotNull(PropertyUtil.containId(fields)) ? true : false; long id = PropertyUtil.getIdValue(fields, t, propertyAccessor); if (isContainsId) { return id > 0L ? update(t) : save(t); } return save(t); } /** * Created By zby on 17:37 2019/1/29 * 保存數(shù)據(jù) */ public T save(T t) { if (CommonUtil.isNull(t)) throw new RuntimeException("不能保存空對象"); PreparedStatement statement = null; ResultSet resultSet = null; StringBuilder columnJoint = new StringBuilder(); StringBuilder columnValue = new StringBuilder(); try { Field[] fields = t.getClass().getDeclaredFields(); String sql = " insert into " + ClassUtil.getClassNameByGenericity(t) + "("; for (int i = 0; i < fields.length; i++) { String propertyName = fields[i].getName(); Object propertyValue = propertyAccessor.getPropertyValue(t, propertyName); if (CommonUtil.isNotNull(propertyValue)) { String columnName = PropertyUtil.propertyNameTransformColumnName(propertyName, true); if (StandardBasicTypes.BOOLEAN.equalsIgnoreCase(fields[i].getGenericType().toString())) { columnJoint.append("is_" + columnName + ","); columnValue.append(propertyValue + ","); } else if (StandardBasicTypes.LONG.equalsIgnoreCase(fields[i].getGenericType().toString()) || StandardBasicTypes.FLOAT.equalsIgnoreCase(fields[i].getGenericType().toString()) || StandardBasicTypes.DOUBLE.equalsIgnoreCase(fields[i].getGenericType().toString()) || StandardBasicTypes.INTEGER.equalsIgnoreCase(fields[i].getGenericType().toString())) { columnJoint.append(columnName + ","); columnValue.append(propertyValue + ","); } else if (StandardBasicTypes.DATE.equalsIgnoreCase(fields[i].getGenericType().toString())) { columnJoint.append(columnName + ","); columnValue.append(""" + DateUtil.SIMPLE_DATE_FORMAT.format((Date) propertyValue) + "","); } else { columnJoint.append(columnName + ","); columnValue.append(""" + propertyValue + "","); } } } columnJoint = StringUtil.replace(columnJoint, ","); columnValue = StringUtil.replace(columnValue, ","); sql += columnJoint + ") VALUES(" + columnValue + ")"; System.out.println(sql); statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); statement.executeUpdate(); resultSet = statement.getGeneratedKeys(); while (resultSet.next()) { return load((Class ) t.getClass(), resultSet.getLong(1)); } return t; } catch (SQLException e) { System.out.println("保存數(shù)據(jù)出錯,實體對象為=" + t); e.printStackTrace(); } finally { SessionClose.closeConnStateResSet(connection, statement, resultSet); } return null; }
測試代碼:
@Test public void testSave() { Session session = new SqlSessionFactory().getCurrentSession(); Project project = new Project(); project.setCustomerName("hhhh"); project.setCreateDatetime(new Date()); project.setDeleted(true); project = (Project) session.save(project); System.out.println(project.getId()); }通過id加載對象
有時,我們只要根據(jù)當(dāng)前對象的id,獲取當(dāng)前對象的全部信息,因而,我們可以這樣寫:
/** * Created By zby on 16:36 2019/1/29 * 通過id獲取對象 */ public T load(Classclazz, Long id) { if (CommonUtil.isNull(clazz)) throw new IllegalArgumentException("參數(shù)為空"); String className = ClassUtil.getClassNameByClass(clazz); String sql = " select * from " + className + " where id= ? "; SqlQuery query = createSqlQuery(sql) .setParamter(0, id) .addEntity(clazz); return (T) query.uniqueResult(); }
測試代碼:
@Test public void testload() { Session session = new SqlSessionFactory().openSession(); Project project = (Project) session.load(Project.class, 4L); System.out.println(project); }回收連接對象
當(dāng)我們使用完該連接對象后,需要將對象放回到容器中,并不是直接調(diào)用connection.close()方法,而是調(diào)用這個方法:
/** * Created By zby on 16:10 2019/3/17 * 獲取容器的對象,如果是關(guān)閉session,則將連接對象放回到容器中 * 如果是開啟session,則從容器中刪除該連接對象 */ protected static List總結(jié)getConnections() { return connections; } /** * Created By zby on 22:45 2019/1/23 * * 當(dāng)關(guān)閉當(dāng)前會話時,這并非真正的關(guān)閉會話 * 只是將連接對象放回到連接池中 */ public static void closeConn(Connection connection) { SqlSessionFactory.getConnections().add(connection); }
寫框架其實是不難的,難就難在如何設(shè)計框架。或者說,難就難在基礎(chǔ)不牢。如果基礎(chǔ)打不牢的話,很難往上攀升。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/73729.html
摘要:官網(wǎng)源碼推薦從開始手寫一個框架更多請在技術(shù)棧微信公眾號后臺回復(fù)關(guān)鍵字。是一個開放源代碼的對象關(guān)系映射框架,它對進(jìn)行了非常輕量級的對象封裝,它將與數(shù)據(jù)庫表建立映射關(guān)系,是一個全自動的框架。 Java 程序員方向太多,且不說移動開發(fā)、大數(shù)據(jù)、區(qū)塊鏈、人工智能這些,大部分 Java 程序員都是 Java Web/后端開發(fā)。那作為一名 Java Web 開發(fā)程序員必須需要熟悉哪些框架呢? 今天...
摘要:要是使用到日歷的話,我們想到使用這個日歷類上面僅僅是我個人總結(jié)的要點,如果有錯誤的地方還請大家給我指正。 納稅服務(wù)系統(tǒng)總結(jié) 納稅服務(wù)系統(tǒng)是我第一個做得比較大的項目(不同于javaWeb小項目),該項目系統(tǒng)來源于傳智Java32期,十天的視頻課程(想要視頻的同學(xué)關(guān)注我的公眾號就可以直接獲取了) 我跟著練習(xí)一步一步完成需求,才發(fā)覺原來Java是這樣用來做網(wǎng)站的,Java有那么多的類庫,頁面...
摘要:一個事務(wù)的執(zhí)行不能被其他事務(wù)干擾。持久性也稱永久性,指一個事務(wù)一旦提交,它對數(shù)據(jù)庫中數(shù)據(jù)的改變就應(yīng)該是永久性的。事務(wù)與相關(guān)聯(lián),并通過調(diào)用實例化。強(qiáng)制此事務(wù)回滾。為此事務(wù)注冊用戶同步回調(diào)。檢查事務(wù)是否成功提交。 一、事務(wù) (1)事務(wù)(Transaction),一般是指要做的或所做的事情。在計算機(jī)術(shù)語中是指訪問并可能更新數(shù)據(jù)庫中各種數(shù)據(jù)項的一個程序執(zhí)行單元(unit)。 (2)事務(wù)應(yīng)該具有...
摘要:一配置屬性詳解可以在各式各樣不同環(huán)境下工作而設(shè)計的因此存在著大量的配置參數(shù)。以簡便操作,多數(shù)配置參數(shù)都有默認(rèn)的配置值也是我們?nèi)粘J褂玫谋仨毱贰? Hibernate (開放源代碼的對象關(guān)系映射框架) Hibernate是一個開放源代碼的對象關(guān)系映射框架,它對JDBC進(jìn)行了非常輕量級的對象封裝, 它將POJO與數(shù)據(jù)庫表建立映射關(guān)系,是一個全自動的orm框架,hibernat...
摘要:前言由于寫的文章已經(jīng)是有點多了,為了自己和大家的檢索方便,于是我就做了這么一個博客導(dǎo)航。 前言 由于寫的文章已經(jīng)是有點多了,為了自己和大家的檢索方便,于是我就做了這么一個博客導(dǎo)航。 由于更新比較頻繁,因此隔一段時間才會更新目錄導(dǎo)航哦~想要獲取最新原創(chuàng)的技術(shù)文章歡迎關(guān)注我的公眾號:Java3y Java3y文章目錄導(dǎo)航 Java基礎(chǔ) 泛型就這么簡單 注解就這么簡單 Druid數(shù)據(jù)庫連接池...
閱讀 635·2021-10-27 14:15
閱讀 1162·2021-10-15 09:42
閱讀 2741·2019-08-30 15:53
閱讀 1280·2019-08-23 17:02
閱讀 2955·2019-08-23 16:23
閱讀 3170·2019-08-23 15:57
閱讀 3457·2019-08-23 14:39
閱讀 512·2019-08-23 14:35