程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2023-06(4)

java实现数据持久层框架,自定义实现数据持久层框架,兼容spring,兼容spring事务管理

发布于2021-06-14 09:40     阅读(952)     评论(0)     点赞(22)     收藏(4)


一、目标

       1:实现一个数据持久层框架,既能像JPA那样不用写sql直接调用框架自带方法操作数据库,又能像mybatis那样执行自定义sql语句。

       2:能够在spring中使用,支持spring事务管理。

       3:能够拦截sql,并在自定义拦截器判断是否执行sql。

       4:插入数据后能选择性将数据库自增列的值写回插入的数据对象。

二、实现思路

       1:新增接口SqlSession,提供数据库操作方法供用户操作。

       2:新增接口SqlGenerator,生成sql语句,供SqlSession实现类调用。

       3:新增接口Executor,执行Sql语句,供SqlSession实现类调用。

       4:新增接口ResultSetHandler,将结果集封装成返回对象,供Executor实现类调用。

       5:新增接口Transaction,内部包含数据库连接(Connection)成员对象,供Executor实现类调用。

       6:核心接口图:

三、核心代码

1:Sql拦截器拦截SqlSession

基于java动态代理实现,Executor拦截与此类似。

  1. @Override
  2. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  3. //执行拦截器
  4. Configuration configuration = this.sqlSession.getConfiguration();
  5. List<Interceptor> interceptorList = configuration.getInterceptorList();
  6. for (Interceptor interceptor : interceptorList) {
  7. List<InterceptorScopeEnum> scopeEnums = interceptor.getInterceptorScopes();
  8. if (scopeEnums == null || scopeEnums.isEmpty() || !scopeEnums.contains( InterceptorScopeEnum.SQL_SESSION )) continue;
  9. if ( !interceptor.process( new Invocation(method, args) ) ) return null;
  10. }
  11. //保存方法返回类型等信息,供处理结果集使用
  12. String methodName = method.getName();
  13. if ( sqlSessionMethodNames.contains(methodName) ){
  14. if ( configuration.getMethodSignature(methodName) == null){
  15. configuration.addMethodSignature(methodName, new MethodSignature(method));
  16. }
  17. }
  18. return method.invoke(this.sqlSession, args);
  19. }

2:封装结果集成返回结果

  1. @Override
  2. public <E> E handleResultSetsForSingle(ResultSet resultSet, Class<E> clazz) throws SQLException {
  3. if ( !resultSet.next() ) return null;
  4. //属性与对应的setter方法,去除sql忽略的属性
  5. Map<String, Method> methodMap = this.getFieldSetterMethodMap(clazz);
  6. //数据库字段名与对象字段名映射关系
  7. Map<String, String> fieldNameMap = EdrHelper.getFieldDbRelateEntity(clazz);
  8. try {
  9. return this.generateRsObj(clazz, resultSet, methodMap, fieldNameMap);
  10. } catch (Exception ex) {
  11. LOG.error("Building result from database Error", ex);
  12. throw new BuildResultException("Building result from database Error", ex);
  13. }
  14. }
  15. private <E> E generateRsObj(Class<E> clazz, ResultSet resultSet, Map<String, Method> methodMap, Map<String, String> fieldNameMap) throws Exception{
  16. ResultSetMetaData metaData = resultSet.getMetaData();
  17. final int columnCount = metaData.getColumnCount();
  18. if (columnCount == 1){
  19. try {
  20. Constructor<E> constructor = clazz.getDeclaredConstructor(clazz);
  21. Object javaObj = configuration.getTypeHandler().jdbcToJavaBeanType(resultSet, 1, clazz);
  22. E instance = constructor.newInstance(javaObj);
  23. //再走一遍javaBean逻辑,尽最大努力避免出错
  24. return this.generateRsByJavaBean(instance, resultSet, methodMap, fieldNameMap);
  25. } catch (Exception ex){}
  26. }
  27. E instance = clazz.getDeclaredConstructor().newInstance();
  28. return this.generateRsByJavaBean(instance, resultSet, methodMap, fieldNameMap);
  29. }
  30. private <E> E generateRsByJavaBean(E instance, ResultSet resultSet, Map<String, Method> methodMap, Map<String, String> fieldNameMap) throws Exception {
  31. if (methodMap.isEmpty() || fieldNameMap.isEmpty()) return instance;
  32. //走javaBean逻辑
  33. ResultSetMetaData metaData = resultSet.getMetaData();
  34. final int columnCount = metaData.getColumnCount();
  35. int i = 0;
  36. TypeHandler typeHandler = configuration.getTypeHandler();
  37. while (i++ < columnCount){
  38. String columnName = metaData.getColumnName(i);
  39. Method method = methodMap.get( fieldNameMap.get(columnName) );
  40. if (method == null){
  41. continue;
  42. }
  43. Object javaObj = typeHandler.jdbcToJavaBeanType(resultSet, i, method.getParameterTypes()[0]);
  44. method.invoke(instance, javaObj);
  45. }
  46. return instance;
  47. }

3:接入Spring事务管理

1:  新增SpringSqlSessionUtil类用于spring环境下SqlSession的获取关闭等操作,spring事务管理原理可参考之前的文章:https://blog.csdn.net/qq_41633199/article/details/115832730

SpringSqlSessionUtil类核心代码。

  1. //sql session是否处于Transactional作用范围
  2. public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
  3. Assert.notNull(session, "No SqlSession specified");
  4. Assert.notNull(sessionFactory, "No SessionFactory specified");
  5. //通过SqlSessionFactory获取以便兼容多数据源情况下含有多个连接池的情况。
  6. SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
  7. return holder != null && holder.getSqlSession() == session;
  8. }
  9. public static SqlSession getSqlSession(SqlSessionFactory sessionFactory) {
  10. Assert.notNull(sessionFactory, "No SqlSession specified");
  11. SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
  12. SqlSession session = sessionTransactionHolder(holder);
  13. if (session != null) {
  14. if ( session.isClosed() ){
  15. LOG.warn("SqlSession has been closed!");
  16. } else {
  17. return session;
  18. }
  19. }
  20. session = sessionFactory.openSession();
  21. //注册到spring事务管理中去
  22. registerSessionHolder(sessionFactory, session);
  23. return session;
  24. }
  25. public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
  26. Assert.notNull(session, "No SqlSession specified");
  27. Assert.notNull(sessionFactory, "No SessionFactory specified");
  28. SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
  29. if (holder != null && holder.getSqlSession() == session) {
  30. holder.released();
  31. } else {
  32. session.close();
  33. }
  34. }
  35. //获取Transaction范围内的sqlSession
  36. private static SqlSession sessionTransactionHolder(SqlSessionHolder holder) {
  37. SqlSession session = null;
  38. if (holder != null && holder.isSynchronizedWithTransaction()) {
  39. holder.requested();
  40. session = holder.getSqlSession();
  41. }
  42. return session;
  43. }
  44. private static void registerSessionHolder(SqlSessionFactory sessionFactory, SqlSession session) {
  45. SqlSessionHolder holder;
  46. if ( TransactionSynchronizationManager.isSynchronizationActive() ) {//当前线程是否存在活跃的事务同步器
  47. TransactionFactory transactionFactory = session.getConfiguration().getTransactionFactory();
  48. if (transactionFactory instanceof SpringManagedTransactionFactory) {
  49. holder = new SqlSessionHolder(session);
  50. //sqlSession写入threadLocal
  51. TransactionSynchronizationManager.bindResource(sessionFactory, holder);
  52. //注册事务回调
  53. TransactionSynchronizationManager.registerSynchronization(new SqlSessionTransactionSyncAdapter(sessionFactory, holder));
  54. holder.setSynchronizedWithTransaction(true);
  55. holder.requested();
  56. } else {
  57. //检查是否设置了数据连接池到spring事务环境
  58. if (TransactionSynchronizationManager.getResource( session.getConfiguration().getDataSource() ) == null) {
  59. if ( LOG.isWarnEnabled() ) {
  60. LOG.warn("SqlSession [{}] was not registered for synchronization because DataSource is not transactional", session);
  61. }
  62. } else {
  63. throw new TransientDataAccessResourceException(
  64. "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
  65. }
  66. }
  67. } else {
  68. if ( LOG.isWarnEnabled() ) {
  69. LOG.warn("SqlSession [{}] was not registered for synchronization because synchronization is not active", session);
  70. }
  71. }
  72. }

2:  SpringSqlSessionUtil类用到的事务回调SqlSessionTransactionSyncAdapter继承了spring事务的TransactionSynchronizationAdapter类,其关键方法如下:

  1. @Override//事务终止回调
  2. public void suspend() {
  3. if (this.sqlSessionHolderActive){
  4. TransactionSynchronizationManager.unbindResource(this.sqlSessionFactory);
  5. }
  6. }
  7. @Override
  8. public void resume() {//重试
  9. if (this.sqlSessionHolderActive){
  10. TransactionSynchronizationManager.bindResource(this.sqlSessionFactory, this.sqlSessionHolder);
  11. }
  12. }
  13. @Override
  14. public void beforeCommit(boolean readOnly) {
  15. if ( TransactionSynchronizationManager.isActualTransactionActive() ){
  16. this.sqlSessionHolder.getSqlSession().commit();
  17. }
  18. }
  19. @Override
  20. public void beforeCompletion() {
  21. if ( !this.sqlSessionHolder.isOpen() ) return;
  22. //从threadLocal移除
  23. TransactionSynchronizationManager.unbindResource(this.sqlSessionFactory);
  24. //释放数据库连接
  25. this.sqlSessionHolder.getSqlSession().close();
  26. }
  27. @Override
  28. public void afterCompletion(int status) {
  29. if (this.sqlSessionHolderActive) {
  30. TransactionSynchronizationManager.unbindResourceIfPossible(this.sqlSessionFactory);
  31. this.sqlSessionHolderActive = false;
  32. this.sqlSessionHolder.getSqlSession().close();
  33. }
  34. this.sqlSessionHolder.reset();
  35. }

3:新增数据库连接容器类SpringManagedTransaction,实现Transaction接口,内部包含数据库连接对象Connection,这里需要在获取数据库连接和关闭数据库连接方法中接入到spring:

  1. public Connection getConnection() throws SQLException {
  2. if (this.connection != null) return this.connection;
  3. this.openConnection();
  4. return this.connection;
  5. }
  6. protected void openConnection() throws SQLException {
  7. if (this.dataSource == null) return;
  8. this.connection = DataSourceUtils.getConnection(this.dataSource);
  9. if (this.connection == null) return;
  10. this.autoCommit = this.connection.getAutoCommit();
  11. this.closed = false;
  12. this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
  13. }
  14. public void close() {
  15. DataSourceUtils.releaseConnection(this.connection, this.dataSource);
  16. this.closed = true;
  17. }

4:  新增类SpringManagedSqlSessionProxy,基于java动态代理,用来将SqlSession接入spring管理中,并支持事务回滚等,核心代码如下:

  1. @Override
  2. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  3. SqlSession sqlSession = SpringSqlSessionUtil.getSqlSession(this.sqlSessionFactory);
  4. try {
  5. Object rs = method.invoke(sqlSession, args);
  6. //没有纳入spring事务中,直接提交
  7. if ( !SpringSqlSessionUtil.isSqlSessionTransactional(sqlSession, this.sqlSessionFactory) ){
  8. sqlSession.commit();
  9. }
  10. return rs;
  11. } catch (Throwable throwable){
  12. throw throwable;
  13. } finally {
  14. if (sqlSession != null){
  15. SpringSqlSessionUtil.closeSqlSession(sqlSession, this.sqlSessionFactory);
  16. }
  17. }
  18. }

5:新增类SpringManagedSqlSessionFactory用于生成SpringManagedSqlSessionProxy代理对象,核心代码:

  1. @Override
  2. public SqlSession openSession() {
  3. SqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(this.configuration, this.sqlGenerator, this.resultSetHandler);
  4. //构建代理对象spring manage
  5. return (SqlSession) Proxy.newProxyInstance(SqlSession.class.getClassLoader(), new Class[]{SqlSession.class}, new SpringManagedSqlSessionProxy(defaultSqlSessionFactory));
  6. }

4:构建spring-boot-starter

        将SqlSessionFactory对象存入spring bean容器,以便业务代码中能自动注入该对象再获取SqlSession。核心代码如下:

  1. /**
  2. *持久化框架 edr自动配置
  3. * @author tfq qq:1394965066
  4. */
  5. @Configuration
  6. @ConditionalOnMissingBean(SqlSessionFactory.class)
  7. @ConditionalOnClass(DefaultSqlSessionFactory.class)
  8. @ConditionalOnBean({DataSource.class, Environment.class})
  9. @AutoConfigureAfter(DataSourceAutoConfiguration.class)//指定顺序
  10. public class SmpEdrAutoConfiguration {
  11. @Bean
  12. public SqlSessionFactory buildDefaultSessionFactory(DataSource dataSource, Environment environment){
  13. LoggerFactory.getLogger(SmpEdrAutoConfiguration.class).debug("Init DefaultSqlSessionFactory of SmpEdr");
  14. //拦截器
  15. String interceptors = environment.getProperty("com.lauor.smpedr.interceptors");
  16. com.lauor.smpedr.Configuration configuration = new com.lauor.smpedr.Configuration(dataSource, new SpringManagedTransactionFactory());
  17. configuration = this.bindInterceptors(interceptors, configuration);
  18. SqlSessionFactory sqlSessionFactory = new SpringManagedSqlSessionFactory(configuration, new SqlGeneratorMysql());
  19. return sqlSessionFactory;
  20. }
  21. //设置拦截器
  22. private com.lauor.smpedr.Configuration bindInterceptors(String interceptors, com.lauor.smpedr.Configuration configuration){
  23. if ( Str.isEmpty(interceptors) ) return configuration;
  24. String[] interceptorArr = interceptors.split(",");
  25. for (String interceptor : interceptorArr) {
  26. try {
  27. Class interceptorCls = ClassUtils.forName(interceptor, this.getClass().getClassLoader());
  28. Constructor constructor = interceptorCls.getDeclaredConstructor();
  29. configuration.addInterceptor( (Interceptor) constructor.newInstance() );
  30. } catch (Exception ex){
  31. LoggerFactory.getLogger(SmpEdrAutoConfiguration.class).error(String.format("error to add interceptor: %s", interceptor), ex);
  32. System.exit(-1);
  33. }
  34. }
  35. return configuration;
  36. }
  37. }

5:springboot使用

      引入依赖后直接通过sqlSessionFactory bean操作DBMS。

6:使用JMeter进行压力测试

   1:测试机器性能参数

        处理器: Intel(R) Core(TM) i7-8550U CPU @ 1.8GHZ

        内存: 8GB

        硬盘: 固态硬盘/嵌入式多媒体控制器 (EMMC) 1 238GB, Micron_1100_MTFDDAV256TBN

        系统:windows10家庭中文版

    2:  测试样例

         一分钟1200次查询单表前10条数据,时间排序,无缓存,总记84万三千次请求。

    3:  测试结果

7:源码地址

     完整代码:https://gitee.com/tandatda/smpedr

     使用完整demo:https://gitee.com/tandatda/demo-edr-smpdb



所属网站分类: 技术文章 > 博客

作者:你不要惹我

链接:http://www.javaheidong.com/blog/article/222670/786f63496bd752008702/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

22 0
收藏该文
已收藏

评论内容:(最多支持255个字符)