यदि आपकी परियोजना के लिए RDBMS का उपयोग ORM या स्प्रिंग Jd...">

लेन-देन, परीक्षण के वातावरण और कॉमन्स-डबल्स पर अन्य सुविधाजनक ऐड-ऑन के लिए समर्थन

लेन-देन, परीक्षण के वातावरण और कॉमन्स-डबल्स पर अन्य सुविधाजनक ऐड-ऑन के लिए समर्थन


"/>
यदि आपकी परियोजना के लिए RDBMS का उपयोग ORM या स्प्रिंग JdbcTemplate के माध्यम से नहीं किया जाता है, तो नंगे JDBC के माध्यम से काम करने से कॉमन्स-डबल्स को बहुत उज्ज्वल किया जा सकता है।
विशेष रूप से यदि आप इसे लेन-देन के साथ जोड़ते हैं, तो विभिन्न वातावरणों के साथ काम करना, ऑटो-ओपनिंग / क्लोजिंग रिसोर्सेस, रीड-ओनली रिक्वेस्ट्स, SqlException को हैंडल करना, इंसर्ट करने पर pk लौटाना, आदि।
उदाहरण के लिए, ताकि आप ऐसा कर सकें:
int userId = Sql.queryForValue("SELECT ID FROM OU.USERS WHERE LAST_NAME=?", "IVANOV"); Sql.update("DELETE FROM OU.USERS WHERE FIRST_NAME=? AND LAST_NAME=?", fname, lname); List<User> list = Sql.query("SELECT FROM OU.USERS", USER_LIST_HANDLER); int newId = Sql.insert("INSERT INTO OU.USERS VALUES (?,?)", fname, lname); 

और यह कोड परिवर्तन और मॉक के बिना एप्लिकेशन सर्वर कन्वेंशन पूल के माध्यम से और JUnit में एक लेनदेन के अंदर काम करेगा।
कॉमन्स-डबल्स के साथ मेरे काम के एक साल से भी कम समय में, उन्होंने विभिन्न वर्गों और उपयोगी तरीकों का अधिग्रहण किया है, जिसके बारे में मैं यहां बात करना चाहता हूं।

अलग-अलग वातावरण में काम करते हैं


DataSourceFactory इंटरफ़ेस के माध्यम से दावों के लिए इनिशियलाइज़ेशन का उपयोग करते हुए, हम JNDI AS में घोषित DataSource प्राप्त करते हैं:
 public class ConnectionFactory { private static final LoggerWrapper LOGGER = LoggerWrapper.get(ConnectionFactory.class); private static class JndiDataSource { private static final DataSource dataSource; static { try { InitialContext ctx = new InitialContext(); dataSource = (DataSource) ctx.lookup("java:/comp/env/jdbc/db_name"); } catch (Exception ex) { throw LOGGER.getIllegalStateException("PostgreSQL initialization failed", ex); } } } interface DataSourceFactory { DataSource getDataSource(); } static DataSourceFactory dataSourceFactory = new DataSourceFactory() { @Override public DataSource getDataSource() { return JndiDataSource.dataSource; } }; public static DataSource getDataSource() { return dataSourceFactory.getDataSource(); } 

परीक्षणों के लिए, इसे कॉमन-डीबीसीपी से बदलें:
  <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> 

 public class TestSq { private static String DB_URL; private static String DB_USER; private static String DB_PSW; private static class DbcpDataSource { private static final BasicDataSource dataSource; static { dataSource = new BasicDataSource(); dataSource.setUrl(DB_URL); dataSource.setUsername(DB_USER); dataSource.setPassword(DB_PSW); } } public static void initDb(String dbUrl, String dbUser, String dbPsw) { DB_URL = dbUrl; DB_USER = dbUser; DB_PSW = dbPsw; ConnectionFactory.dataSourceFactory = new ConnectionFactory.DataSourceFactory() { @Override public DataSource getDataSource() { return DbcpDataSource.dataSource; } }; } 

यदि डेटाबेस में क्रेडेंशियल मावेन सेटिंग्स में हैं। xml प्रोफाइल, तो आप मावेन प्रोफाइल के आधार पर परीक्षणों को आरंभ कर सकते हैं।
हम डेटाबेस की साख के साथ परीक्षण / संसाधन / प्रोजेक्ट बनाते हैं।
  db.url=${db.url} db.user=${db.user} db.password=${db.password} 

इसे सेटिंग्स से चर के साथ फ़िल्टर करें:
  <testResources> <testResource> <filtering>true</filtering> </testResource> 

और getResourceAsStream द्वारा संपत्तियों को लोड करें:
 public static void initProfileDb() { Properties prop = UtilProperties.loadAsResource("project.properties"); initDb(prop.getProperty("db.url"), prop.getProperty("db.user"), prop.getProperty("db.password")); } 

यह TestSql.initProfileDb () को प्रारंभ करने के लिए परीक्षणों की शुरुआत में रहता है और चयनित प्रोफ़ाइल (mvn -P env परीक्षण) के डेटाबेस के सभी प्रश्न कॉमन्स-dbcp के माध्यम से जाएंगे।

संसाधनों और अपवादों के साथ काम करें


ConnectionFactory में एक कनेक्शन जोड़ें और इसे बंद करें:
 static Connection getConnection(boolean isReadOnly) throws SQLException { Connection conn = getDataSource().getConnection(); if (isReadOnly) { conn.setReadOnly(true); } return conn; } static void close(Connection conn) { if (conn != null) { try { if (conn.isReadOnly()) { conn.setReadOnly(false); // restore NOT readOnly before return to pool } conn.close(); } catch (SQLException e) { Sql.warn(e); } } } 

हम इंटरफ़ेस की घोषणा करते हैं जहां अनुरोध तर्क वास्तव में होगा
 public interface SqlExecutor<T> { T run(Connection conn) throws SQLException; } 

और इसके आवरण:
 public class Sql { public static <T> T execute(boolean isReadOnly, SqlExecutor<T> executor) { try { return executor.run(ConnectionFactory.getConnection(isReadOnly) } catch (SQLException e) { throw LOGGER.getStateException(ExceptionType.DATA_BASE, e); } finally { ConnectionFactory.close(conn); } } 

अब सभी डेटाबेस प्रश्न हमारे आवरण के माध्यम से जाएंगे:
 private static final QueryRunner QUERY_RUNNER = new QueryRunner(); public static int update(Connection conn, final String updateSql, final Object... params) throws SQLException { return QUERY_RUNNER.update(conn, updateSql, params); } public static <T> T query(Connection conn, final String sql, final ResultSetHandler<T> rsh, final Object... params) throws SQLException { return QUERY_RUNNER.query(conn, sql, rsh, params); } public static int update(final String updateSql, final Object... params) { return Sql.execute(false, new SqlExecutor<Integer>() { @Override public Integer run(Connection conn) throws SQLException { return update(conn, updateSql, params); } }); } public static <T> T query(final String sql, final ResultSetHandler<T> rsh, final Object... params) { return execute(true, new SqlExecutor<T>() { @Override public T run(Connection conn) throws SQLException { return query(conn, sql, rsh, params); } }); } 

हम स्क्रैप विधि के माध्यम से स्केलर प्राप्त करते हैं:
 public static <T> T queryForValue(Connection conn, final String sql, final Object... params) throws SQLException { return query(conn, sql, new ScalarHandler<T>(), params); } public static <T> T queryForValue(final String sql, final Object... params) throws SQLException { return query(sql, new ScalarHandler<T>(), params); } 

डालने पर पी.के.


अनारक्षित स्थिति में अद्यतनों के लिए मुख्य कुंजी जनरेट करते समय, आपको स्वयं सब कुछ करना होगा:
 public class KeyQueryRunner extends QueryRunner { private static final LoggerWrapper LOGGER = LoggerWrapper.get(KeyQueryRunner.class); private static final ResultSetHandler<Integer> KEY_HANDLER = new ScalarHandler<Integer>(); public int insert(Connection conn, String sql, Object... params) throws SQLException { PreparedStatement stmt = null; int rows = 0; try { stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); this.fillStatement(stmt, params); rows = stmt.executeUpdate(); if (rows != 1) { throw LOGGER.getStateException("   " + sql, ExceptionType.DATA_BASE); } return KEY_HANDLER.handle(stmt.getGeneratedKeys()); } catch (SQLException e) { this.rethrow(e, sql, params); } finally { close(stmt); } return rows; } } private static final KeyQueryRunner KEY_QUERY_RUNNER = new KeyQueryRunner(); public static int insert(Connection conn, final String insertSql, final Object... params) throws SQLException { return KEY_QUERY_RUNNER.insert(conn, insertSql, params); } public static int insert(final String insertSql, final Object... params) { return Sql.execute(false, new SqlExecutor<Integer>() { @Override public Integer run(Connection conn) throws SQLException { return insert(conn, insertSql, params); } }); } 

यदि कुंजियाँ लंबी हैं - तो int-> long, Integer-> Long को बदलना न भूलें।

लेन-देन संबंधी


अंत में, लेनदेन के बिना डेटाबेस के साथ क्या काम है?
मान लें कि हमारे पास कुछ तरीका है जो डेटाबेस तक पहुँचता है:
 checkAssess(final int docId, final Access accessMode) 

वह कनेक्शन स्वीकार नहीं करता है, लेकिन हम इसे लेनदेन के अंदर उपयोग करना चाहते थे।
और मैं चाहूंगा कि इसमें कोई नक़ल न हो
 checkAssess(Connection conn, final int docId, final Access accessMode) 

एक लेन-देन आवरण बनाना:
 public static <T> T executeInTransaction(SqlExecutor<T> executor) { Connection conn = null; try { conn = ConnectionFactory.getTxConnection(); T res = executor.run(conn); conn.commit(); return res; } catch (Error e) { throw rollback(conn, e); } catch (Exception e) { throw rollback(conn, e); } finally { ConnectionFactory.closeTx(conn); } } private static StateException rollback(Connection conn, Throwable e) { try { if (conn != null) { conn.rollback(); } return LOGGER.getStateException(ExceptionType.DATA_BASE, e); } catch (SQLException se) { return LOGGER.getStateException("Unable to rollback transaction", ExceptionType.DATA_BASE, e); } } 

और लेन-देन कनेक्शन डालें
 public class ConnectionFactory { ... private static final ThreadLocal<Connection> TRANSACT_CONN = new ThreadLocal<Connection>(); static Connection getCurrentConn() { return TRANSACT_CONN.get(); } static Connection getTxConnection() throws SQLException { Connection conn = TRANSACT_CONN.get(); if (conn != null) { throw LOGGER.getIllegalStateException("Start second transaction in one thread"); } conn = getDataSource().getConnection(); conn.setAutoCommit(false); TRANSACT_CONN.set(conn); return conn; } static void closeTx(Connection conn) { close(conn); TRANSACT_CONN.set(null); } 

और हम अपने निष्पादन में लेनदेन की संभावना को ध्यान में रखते हैं:
 public static <T> T execute(boolean isReadOnly, SqlExecutor<T> executor) { Connection conn = null; Connection txConn = ConnectionFactory.getCurrentConn(); try { return executor.run( (txConn == null) ? ConnectionFactory.getConnection(isReadOnly) : txConn); } catch (SQLException e) { throw LOGGER.getStateException(ExceptionType.DATA_BASE, e); } finally { if (txConn == null) ConnectionFactory.close(conn); } } 

अब, लेन-देन के अंदर Execute के माध्यम से डेटाबेस में हमारी कोई भी क्वेरी लेन-देन कनेक्शन के साथ काम करेगी।
लेकिन यह भी संभव है कि एक्सेक्यूटर के माध्यम से लेनदेन के अंदर डेटाबेस का उपयोग किया जाए:
  List<Person> list = Sql.executeInTransaction(new SqlExecutor<List<Person>>() { @Override public List<Person> run(Connection conn) throws SQLException { Sql.select(conn, ...); Sql.update(conn, ...); return Sql.select(conn, ...); } }); 

दृष्टिकोण की तुलना में सरल है, उदाहरण के लिए, स्प्रिंग लेन-देन प्रबंधक, कोई नेस्टेड लेनदेन नहीं है, लेकिन 99% मामलों के लिए यह पर्याप्त है। खासकर यदि आप संपूर्ण स्प्रिंग या ORM को खींचना नहीं चाहते हैं।

अंत में, प्रकार शून्य और वापसी शून्य नहीं लिखने के लिए, हम परिणाम के बिना क्वेरी करना संभव बना देंगे (वसंत लेनदेन का एनालॉगसालबैकविथाउट। परिणाम)
 public interface SqlExecutorVoid { void run(Connection conn) throws SQLException; } public class Sql { ... private static SqlExecutor<Void> getWrapperExecutor(final SqlExecutorVoid voidExecutor) { return new SqlExecutor<Void>() { @Override public Void run(Connection conn) throws SQLException { voidExecutor.run(conn); return null; } }; } public static void execute(boolean isReadOnly, SqlExecutorVoid executor) { execute(isReadOnly, getWrapperExecutor(executor)); } public static void executeInTransaction(SqlExecutorVoid executor) { executeInTransaction(getWrapperExecutor(executor)); } 

यदि आपको दृष्टिकोण पसंद आया है, तो अपने डेटाबेस के साथ सरल काम के लिए आपके पास सुविधाजनक (और पहले से परिचित) टूल से कक्षाएं एकत्र करना होगा।
आपका ध्यान देने के लिए धन्यवाद। परियोजनाओं के लिंक जो पहले से ही समान और रचनात्मक आलोचना को लागू कर रहे हैं, उनका स्वागत है।

पुनश्च: जब बीन्स के लिए बीनहैंडलर और बीनलिस्टहैंडलर मैपिंग रिजल्टसेट का उपयोग करते हैं, तो डिफ़ॉल्ट कंस्ट्रक्टर और परिणामी कक्षाओं के सेटर्स के बारे में मत भूलना।

Source: https://habr.com/ru/post/In183204/


All Articles