前言

  前文讲述了net.sz.framework 框架的基础实现功能,本文主讲 net.sz.framework.db 和 net.sz.framework.szthread;

net.sz.framework.db 是 net.sz.framework 底层框架下的orm框架,仿照翻译了hibernate实现功能,虽然不足hibernate强大;但在于其功能实现单一高效和高可控性;

net.sz.framework.szthread 是 net.sz.framework 底层框架下的线程控制中心和线程池概念;

以上就不在赘述,前面的文章已经将结果了;

叙述

  无论你是做何种软件开发,都离不开数据;

数据一般我们都会有两个问题一直在脑后徘徊,那就是读和写的问题;

一般正常情况下数据我们可能出现的存储源是数据库(mysql,sqlserver,sqlite,Nosql等)、文件数据库(excel,xml,cvs等)

无论是合作数据格式都只是在意数据的存储;保证数据不丢失等情况;

那么我们为了数据的读取和写入高效会想尽办法去处理数据,已达到我们需求范围类的数据最高效最稳当的方式;

今天我们准备的是 orm框架下面的 SqliteDaoImpl 对 sqlite数据源 进行测试和代码设计;换其他数据源也是大同小异;

准备工作

新建项目 maven java项目 net.sz.dbserver

我们在项目下面创建model、cache、db、main这几个包;

然后在 model 包 下面创建 ModelTest 类

 package net.sz.dbserver.model;

 import javax.persistence.Id;
 import net.sz.framework.szlog.SzLogger;

 /**
  *
  * <br>
  * author 失足程序员<br>
  * blog http://www.cnblogs.com/ty408/<br>
  * mail 492794628@qq.com<br>
  * phone 13882122019<br>
  */
 public class ModelTest {

     private static SzLogger log = SzLogger.getLogger();

     /*主键ID*/
     @Id
     private long Id;
     /*内容*/
     private String name;

     public ModelTest() {
     }

     public long getId() {
         return Id;
     }

     public void setId(long Id) {
         this.Id = Id;
     }

     public String getName() {
         return name;
     }

     public void setName(String name) {
         this.name = name;
     }

 }

然后在db包下面建立dbmanager类;

 package net.sz.dbserver.db;

 import java.sql.Connection;
 import java.util.ArrayList;
 import net.sz.dbserver.model.ModelTest;
 import net.sz.framework.db.Dao;
 import net.sz.framework.db.SqliteDaoImpl;
 import net.sz.framework.szlog.SzLogger;
 import net.sz.framework.utils.PackageUtil;

 /**
  *
  * <br>
  * author 失足程序员<br>
  * blog http://www.cnblogs.com/ty408/<br>
  * mail 492794628@qq.com<br>
  * phone 13882122019<br>
  */
 public class DBManager {

     private static SzLogger log = SzLogger.getLogger();
     private static final DBManager IN_ME = new DBManager();

     public static DBManager getInstance() {
         return IN_ME;
     }

     Dao dao = null;

     public DBManager() {
         try {
             /*不使用连接池,显示执行sql语句的数据库操作*/
             this.dao = new SqliteDaoImpl("/home/sqlitedata/testdb.dat", true);
         } catch (Exception e) {
             log.error("创建数据库连接", e);
         }
     }

     /**
      * 检查并创建数据表结构
      */
     public void checkTables() {
         /*创建连接,并自动释放*/
         try (Connection con = this.dao.getConnection()) {
             String packageName = "net.sz.dbserver.model";
             /*获取包下面所有类*/
             ArrayList<Class<?>> tables = PackageUtil.getClazzs(packageName);
             if (tables != null) {
                 for (Class<?> table : tables) {
                     /*检查是否是需要创建的表*/
                     if (this.dao.checkClazz(table)) {
                         /*创建表结构*/
                         this.dao.createTable(con, table);
                     }
                 }
             }
         } catch (Exception e) {
             log.error("创建表抛异常", e);
         }
     }

 }

我们在dbmanager类里面通过SqliteDaoImpl 类创建了sqlite数据库支持的类似于hibernate的辅助;

在checktables下面会查找我们项目包下面所有类型,并且创建数据表;如果表存在就更新表结构(sqlite特性,不会更新表结构);

我们在checktables函数下面做到了对连接的复用情况;创建后并自动释放代码

接下来main包里面创建主函数启动类

 package net.sz.dbserver.main;

 import net.sz.dbserver.db.DBManager;
 import net.sz.framework.szlog.SzLogger;

 /**
  *
  * <br>
  * author 失足程序员<br>
  * blog http://www.cnblogs.com/ty408/<br>
  * mail 492794628@qq.com<br>
  * phone 13882122019<br>
  */
 public class MainManager {

     private static SzLogger log = SzLogger.getLogger();

     public static void main(String[] args) {
         log.error("创建数据库,创建数据表结构");
         DBManager.getInstance().checkTables();
     }

 }

以上代码我们完成了数据库文件和数据表的创建

 --- exec-maven-plugin:1.2.1:exec (default-cli) @ net.sz.dbserver ---
 设置系统字符集sun.stdout.encoding:utf-8
 设置系统字符集sun.stderr.encoding:utf-8
 日志级别:DEBUG
 输出文件日志目录:../log/sz.log
 是否输出控制台日志:true
 是否输出文件日志:true
 是否使用双缓冲输出文件日志:true
 [04-07 10:56:38:198:ERROR:MainManager.main():19] 创建数据库,创建数据表结构
 [04-07 10:56:38:521:ERROR:Dao.getColumns():532] 类:net.sz.dbserver.model.ModelTest 字段:log is transient or static or final;
 [04-07 10:56:38:538:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 无此表
 [04-07 10:56:38:561:ERROR:SqliteDaoImpl.createTable():200]
 表:
  create table if not exists `ModelTest` (
      `Id` bigint not null primary key,
      `name` varchar(255) null
 );
 创建完成;

这里的步骤在之前文章《存在即合理,重复轮子orm java版本》里面有详细介绍,不过当前版本和当时文章版本又有更多优化和改进;

准备测试数据

        /*创建支持id*/
         GlobalUtil.setServerID(1);
         for (int i = 0; i < 10; i++) {
             ModelTest modelTest = new ModelTest();
             /*获取全局唯一id*/
             modelTest.setId(GlobalUtil.getId());
             /*设置参数*/
             modelTest.setName("123");

             try {
                 DBManager.getInstance().getDao().insert(modelTest);
             } catch (Exception e) {
                 log.error("写入数据失败", e);
             }
         }    

输出

 --- exec-maven-plugin:1.2.1:exec (default-cli) @ net.sz.dbserver ---
 设置系统字符集sun.stdout.encoding:utf-8
 设置系统字符集sun.stderr.encoding:utf-8
 日志级别:DEBUG
 输出文件日志目录:../log/sz.log
 是否输出控制台日志:true
 是否输出文件日志:true
 是否使用双缓冲输出文件日志:true
 [04-07 11:13:17:904:ERROR:MainManager.main():21] 创建数据库,创建数据表结构
 [04-07 11:13:18:203:ERROR:Dao.getColumns():532] 类:net.sz.dbserver.model.ModelTest 字段:log is transient or static or final;
 [04-07 11:13:18:215:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在
 [04-07 11:13:18:216:ERROR:SqliteDaoImpl.existsColumn():130] 数据库:null 表:ModelTest 映射数据库字段:ModelTest 检查结果:已存在,将不会修改
 [04-07 11:13:18:216:ERROR:SqliteDaoImpl.createTable():168] 表:ModelTest 字段:Id 映射数据库字段:Id 存在,将不会修改,
 [04-07 11:13:18:216:ERROR:SqliteDaoImpl.existsColumn():130] 数据库:null 表:ModelTest 映射数据库字段:ModelTest 检查结果:已存在,将不会修改
 [04-07 11:13:18:216:ERROR:SqliteDaoImpl.createTable():168] 表:ModelTest 字段:name 映射数据库字段:name 存在,将不会修改,
 [04-07 11:13:18:245:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在
 [04-07 11:13:18:245:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest
 [04-07 11:13:18:246:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@4bf558aa 添加数据 表:ModelTest 结果 影响行数:1
 [04-07 11:13:18:272:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在
 [04-07 11:13:18:272:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest
 [04-07 11:13:18:273:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@2d38eb89 添加数据 表:ModelTest 结果 影响行数:1
 [04-07 11:13:18:295:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在
 [04-07 11:13:18:296:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest
 [04-07 11:13:18:297:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@5fa7e7ff 添加数据 表:ModelTest 结果 影响行数:1
 [04-07 11:13:18:319:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在
 [04-07 11:13:18:319:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest
 [04-07 11:13:18:320:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@4629104a 添加数据 表:ModelTest 结果 影响行数:1
 [04-07 11:13:18:343:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在
 [04-07 11:13:18:343:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest
 [04-07 11:13:18:344:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@27f8302d 添加数据 表:ModelTest 结果 影响行数:1
 [04-07 11:13:18:368:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在
 [04-07 11:13:18:368:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest
 [04-07 11:13:18:369:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@4d76f3f8 添加数据 表:ModelTest 结果 影响行数:1
 [04-07 11:13:18:391:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在
 [04-07 11:13:18:391:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest
 [04-07 11:13:18:392:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@2d8e6db6 添加数据 表:ModelTest 结果 影响行数:1
 [04-07 11:13:18:415:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在
 [04-07 11:13:18:415:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest
 [04-07 11:13:18:416:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@23ab930d 添加数据 表:ModelTest 结果 影响行数:1
 [04-07 11:13:18:438:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在
 [04-07 11:13:18:439:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest
 [04-07 11:13:18:440:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@4534b60d 添加数据 表:ModelTest 结果 影响行数:1
 [04-07 11:13:18:461:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在
 [04-07 11:13:18:462:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest
 [04-07 11:13:18:463:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@3fa77460 添加数据 表:ModelTest 结果 影响行数:1

重构modeltest类

首先在cache包下面创建CacheBase类实现缓存的基本参数

 package net.sz.dbserver.cache;

 import javax.persistence.Id;
 import net.sz.framework.util.AtomInteger;

 /**
  *
  * <br>
  * author 失足程序员<br>
  * blog http://www.cnblogs.com/ty408/<br>
  * mail 492794628@qq.com<br>
  * phone 13882122019<br>
  */
 public class CacheBase {

     /*主键ID*/
     @Id
     protected long Id;

     /*编辑状态 是 transient 字段,不会更新到数据库的*/
     private volatile transient boolean edit;
     /*版本号 是 transient 字段,不会更新到数据库的*/
     private volatile transient AtomInteger versionId;
     /*创建时间*/
     private volatile transient long createTime;
     /*最后获取缓存时间*/
     private volatile transient long lastGetCacheTime;

     public CacheBase() {
     }

     /**
      * 创建
      */
     public void createCache() {
         edit = false;
         versionId = new AtomInteger(1);
         createTime = System.currentTimeMillis();
         lastGetCacheTime = System.currentTimeMillis();
     }

     public long getId() {
         return Id;
     }

     public void setId(long Id) {
         this.Id = Id;
     }

     public boolean isEdit() {
         return edit;
     }

     public void setEdit(boolean edit) {
         this.edit = edit;
     }

     public AtomInteger getVersionId() {
         return versionId;
     }

     public void setVersionId(AtomInteger versionId) {
         this.versionId = versionId;
     }

     public long getCreateTime() {
         return createTime;
     }

     public void setCreateTime(long createTime) {
         this.createTime = createTime;
     }

     public long getLastGetCacheTime() {
         return lastGetCacheTime;
     }

     public void setLastGetCacheTime(long lastGetCacheTime) {
         this.lastGetCacheTime = lastGetCacheTime;
     }

     /**
      * 拷贝数据
      *
      * @param cacheBase
      */
     public void copy(CacheBase cacheBase) {
         this.Id = cacheBase.Id;
     }

 }

在cachebase类中,我创建了copy函数用来赋值新数据的;

通过这个类型,我们可以做到定时缓存,滑动缓存效果;

增加版号的作用在于,更新操作标识,是否是编辑状态也是用作更新标识;

于此同时我们把原 ModelTest 唯一键、主键 id 移动到了 cachebase 父类中

修改modeltest类继承cachebase;

 public class ModelTest extends CacheBase

改造一下dbmanager

     Dao readDao = null;
     Dao writeDao = null;

     public Dao getReadDao() {
         return readDao;
     }

     public Dao getWriteDao() {
         return writeDao;
     }

     public DBManager() {
         try {
             /*不使用连接池,显示执行sql语句的数据库操作*/
             this.readDao = new SqliteDaoImpl("/home/sqlitedata/testdb.dat", true);
             /*不使用连接池,显示执行sql语句的数据库操作*/
             this.writeDao = new SqliteDaoImpl("/home/sqlitedata/testdb.dat", true);
         } catch (Exception e) {
             log.error("创建数据库连接", e);
         }
     }

加入读取数据库连接、写入数据库连接;

CacheManager

在cache包下面建立cachemanager类;

cachemanager 类型是我们具体和重点思路;

构建了读取,并加入缓存集合;

构建了更新并写入数据库;

同时读取和更新都保证线程安全性特点;

 package net.sz.dbserver.cache;

 import java.util.concurrent.ConcurrentHashMap;
 import net.sz.dbserver.db.DBManager;
 import net.sz.framework.szlog.SzLogger;

 /**
  *
  * <br>
  * author 失足程序员<br>
  * blog http://www.cnblogs.com/ty408/<br>
  * mail 492794628@qq.com<br>
  * phone 13882122019<br>
  */
 public class CacheManager {

     private static SzLogger log = SzLogger.getLogger();
     private static final CacheManager IN_ME = new CacheManager();

     public static CacheManager getInstance() {
         return IN_ME;
     }
     /*缓存集合*/
     final ConcurrentHashMap<Long, CacheBase> cacheMap = new ConcurrentHashMap<>();

     /**
      * 获取一条数据,这里我只是测试,提供思路,
      * <br>
      * 所以不会去考虑list等情况;
      * <br>
      * 需要的话可以自行修改
      *
      * @param <T>
      * @param clazz
      * @param id
      * @return
      */
     public <T extends CacheBase> T getCacheBase(Class<T> clazz, long id) {
         CacheBase cacheBase = null;
         cacheBase = cacheMap.get(id);
         if (cacheBase == null) {
             try {
                 /*先读取数据库*/
                 cacheBase = DBManager.getInstance().getReadDao().getObjectByWhere(clazz, "where id=@id", id);
                 /*加入同步操作*/
                 synchronized (cacheMap) {
                     /*这个时候再次读取缓存,防止并发*/
                     CacheBase tmp = cacheMap.get(id);
                     /*双重判断*/
                     if (tmp == null) {
                         /*创建缓存标识*/
                         cacheBase.createCache();
                         /*加入缓存信息*/
                         cacheMap.put(id, cacheBase);
                     } else {
                         cacheBase = tmp;
                     }
                 }
             } catch (Exception e) {
                 log.error("读取数据异常", e);
             }
         }

         if (cacheBase != null) {
             /*更新最后获取缓存的时间*/
             cacheBase.setLastGetCacheTime(System.currentTimeMillis());
         }

         return (T) cacheBase;
     }

     /**
      * 更新缓存数据同时更新数据库数据
      *
      * @param <T>
      * @param t
      * @return
      */
     public <T extends CacheBase> boolean updateCacheBase(T t) {
         if (t == null) {
             throw new UnsupportedOperationException("参数 T 为 null");
         }
         try {
             CacheBase cacheBase = null;
             cacheBase = cacheMap.get(t.getId());
             /*理论上,控制得当这里是不可能为空的*/
             if (cacheBase != null) {
                 /*理论上是能绝对同步的,你也可以稍加修改*/
                 synchronized (cacheBase) {
                     /*验证编辑状态和版号,保证写入数据是绝对正确的*/
                     if (cacheBase.isEdit()
                             && cacheBase.getVersionId() == t.getVersionId()) {
                         /*拷贝最新数据操作*/
                         cacheBase.copy(t);
                         /*写入数据库,用不写入还是同步写入,看自己需求而一定*/
                         DBManager.getInstance().getWriteDao().update(cacheBase);
                         /*保证写入数据库后进行修改 对版本号进行加一操作*/
                         cacheBase.getVersionId().changeZero(1);
                         /*设置最新的最后访问时间*/
                         cacheBase.setLastGetCacheTime(System.currentTimeMillis());
                         /*修改编辑状态*/
                         cacheBase.setEdit(false);
                         log.error("数据已修改,最新版号:" + cacheBase.getVersionId());
                         return true;
                     } else {
                         log.error("版本已经修改无法进行更新操作");
                         throw new UnsupportedOperationException("版本已经修改无法进行更新操作");
                     }
                 }
             } else {
                 log.error("缓存不存在无法修改数据");
                 throw new UnsupportedOperationException("缓存不存在无法修改数据");
             }
         } catch (Exception e) {
             throw new UnsupportedOperationException("更新数据异常", e);
         }
     }

     /**
      * 获取独占编辑状态
      *
      * @param id
      * @return
      */
     public boolean updateEdit(long id) {
         CacheBase t = null;
         t = cacheMap.get(id);
         if (t == null) {
             throw new UnsupportedOperationException("未找到数据源");
         }
         return updateEdit(t);
     }

     /**
      * 获取独占编辑状态
      *
      * @param t
      * @return
      */
     public boolean updateEdit(CacheBase t) {
         if (t == null) {
             throw new UnsupportedOperationException("参数 T 为 null");
         }
         if (!t.isEdit()) {
             synchronized (t) {
                 if (!t.isEdit()) {
                     /*同步后依然需要双重判定*/
                     t.setEdit(true);
                     return true;
                 }
             }
         }
         return false;
     }

 }

可能有人要问, 为啥要加锁,加版号或者加编辑状态;

我们先看一张图片

当同一份数据,展示给客户端(web,多线程等)的时候,同时进行获取,进行编辑,我们不可能每次都需要去调用独占编辑;

那么问题来了我们就拿modeltest的name字段说明,当前等于123,当client1和client2都表示数据的名字错误了需要修改成789;

那么在写入数据库的时候总会有先后顺序,那么后面的很可能就覆盖了前面的修改,

我们假如client1先提交,把name字段改为456,这时候client2提交了,789就直接覆盖了456字段,

程序根本不知道字段的覆盖了,也不知道哪一个是正确的;

所以我加入了编辑状态和版号验证;当然你也可以根据你的需求来进行修改

 package net.sz.dbserver.main;

 import net.sz.dbserver.cache.CacheManager;
 import net.sz.dbserver.db.DBManager;
 import net.sz.dbserver.model.ModelTest;
 import net.sz.framework.szlog.SzLogger;
 import net.sz.framework.utils.GlobalUtil;

 /**
  *
  * <br>
  * author 失足程序员<br>
  * blog http://www.cnblogs.com/ty408/<br>
  * mail 492794628@qq.com<br>
  * phone 13882122019<br>
  */
 public class MainManager {

     private static SzLogger log = SzLogger.getLogger();

     public static void main(String[] args) {

         log.error("创建数据库,创建数据表结构");
         DBManager.getInstance().checkTables();
         /*创建支持id*/
         GlobalUtil.setServerID(1);
         ModelTest modelTest = new ModelTest();
         /*获取全局唯一id*/
         modelTest.setId(GlobalUtil.getId());
         /*设置参数*/
         modelTest.setName("123");

         /*创建测试数据先修改数据库*/
         try {
             DBManager.getInstance().getReadDao().insert(modelTest);
         } catch (Exception e) {
             log.error("写入数据失败", e);
         }

         /*打印一次id*/
         log.error("modelTest.getId()=" + modelTest.getId());

         for (int i = 0; i < 3; i++) {
             new Thread(() -> {
                 try {
                     /*上面的写入数据是为了获取这个id,保证测试代码编辑功能*/
                     ModelTest cacheBase = CacheManager.getInstance().getCacheBase(ModelTest.class, modelTest.getId());
                     if (cacheBase != null) {
                         log.error("成功获得数据");
                         /*独占编辑状态你可以不需要*/
                         if (CacheManager.getInstance().updateEdit(cacheBase)) {
                             log.error("成功获得编辑状态");
                             /*为了模拟并发,我们采用id,保证唯一的数据查看到底谁写入成功*/
                             cacheBase.setName(GlobalUtil.getId() + "");
                             CacheManager.getInstance().updateCacheBase(cacheBase);
                             log.error("modelTest.getName()=" + cacheBase.getName());
                         } else {
                             log.error("获取编辑状态失败");
                         }
                     }
                 } catch (Exception e) {
                     log.error("更新数据异常", e);
                 }
             }).start();
         }

     }

 }

在mainmanager类main函数测试里面加入3个线程模拟并发状态

正常添加的测试数据

 [04-07 13:50:50:514:ERROR:MainManager.main():23] 创建数据库,创建数据表结构
 [04-07 13:50:50:937:ERROR:Dao.getColumns():532] 类:net.sz.dbserver.model.ModelTest 字段:log is transient or static or final;
 [04-07 13:50:50:952:ERROR:Dao.getColumns():532] 类:net.sz.dbserver.cache.CacheBase 字段:edit is transient or static or final;
 [04-07 13:50:50:952:ERROR:Dao.getColumns():532] 类:net.sz.dbserver.cache.CacheBase 字段:versionId is transient or static or final;
 [04-07 13:50:50:952:ERROR:Dao.getColumns():532] 类:net.sz.dbserver.cache.CacheBase 字段:createTime is transient or static or final;
 [04-07 13:50:50:952:ERROR:Dao.getColumns():532] 类:net.sz.dbserver.cache.CacheBase 字段:lastGetCacheTime is transient or static or final;
 [04-07 13:51:37:591:ERROR:MainManager.main():41] modelTest.getId()=7040713505000100000
 [04-07 13:51:45:392:ERROR:MainManager.lambda$main$0():49] 成功获得数据
 [04-07 13:51:45:392:ERROR:MainManager.lambda$main$0():49] 成功获得数据
 [04-07 13:51:45:392:ERROR:MainManager.lambda$main$0():49] 成功获得数据
 [04-07 13:51:45:392:ERROR:MainManager.lambda$main$0():52] 成功获得编辑状态
 [04-07 13:51:45:392:ERROR:MainManager.lambda$main$0():58] 获取编辑状态失败
 [04-07 13:51:45:392:ERROR:MainManager.lambda$main$0():58] 获取编辑状态失败
 [04-07 13:51:45:428:ERROR:CacheManager.updateCacheBase():101] 数据已修改,最新版号:2
 [04-07 13:51:45:428:ERROR:MainManager.lambda$main$0():56] modelTest.getName()=7040713514500100000

修改后的数据;

保证了并发写入、修改的问题,保证了数据的一致性;

实现滑动缓存

在cache包下面建立里CheckCacheTimer定时器类

 package net.sz.dbserver.cache;

 import java.util.HashMap;
 import java.util.Map;
 import net.sz.framework.szlog.SzLogger;
 import net.sz.framework.szthread.TimerTaskModel;

 /**
  *
  * <br>
  * author 失足程序员<br>
  * blog http://www.cnblogs.com/ty408/<br>
  * mail 492794628@qq.com<br>
  * phone 13882122019<br>
  */
 public class CheckCacheTimer extends TimerTaskModel {

     private static SzLogger log = SzLogger.getLogger();

     public CheckCacheTimer(int intervalTime) {
         super(intervalTime);
     }

     @Override
     public void run() {
         /*考虑缓存的清理的都放在这里、当然有很多值的注意细节有待细化*/
         HashMap<Long, CacheBase> tmp = new HashMap(CacheManager.getInstance().cacheMap);
         for (Map.Entry<Long, CacheBase> entry : tmp.entrySet()) {
             Long key = entry.getKey();
             CacheBase value = entry.getValue();
             if (!value.isEdit()) {
                 /*如果数据不在编辑状态、且30分钟无访问清理*/
                 if (System.currentTimeMillis() - value.getLastGetCacheTime() > 30 * 60 * 1000) {
                     synchronized (CacheManager.getInstance().cacheMap) {
                         if (!value.isEdit()) {
                             /*如果数据不在编辑状态、且30分钟无访问清理*/
                             if (System.currentTimeMillis() - value.getLastGetCacheTime() > 30 * 60 * 1000) {
                                 CacheManager.getInstance().cacheMap.remove(value.getId());
                             }
                         }
                     }
                 }
             }
         }
     }
 }

在cachemanager类构造函数加入

     public CacheManager() {
         /*创建一秒钟检查的定时器*/
         ThreadPool.addTimerTask(ThreadPool.GlobalThread, new CheckCacheTimer(1000));
     }

滑动缓存就构建完成了,

这里就不在测试了,理论就是这么个理论;思路就是这么个思路;

脱离数据源的单纯缓存器

改造CacheBase类

 package net.sz.net.sz.framework.caches;

 import net.sz.framework.util.AtomInteger;

 /**
  *
  * <br>
  * author 失足程序员<br>
  * blog http://www.cnblogs.com/ty408/<br>
  * mail 492794628@qq.com<br>
  * phone 13882122019<br>
  */
 public abstract class CacheBase {

     /*编辑状态 */
     private volatile transient boolean edit;
     /*版本号 */
     private volatile transient AtomInteger versionId;
     /*创建时间*/
     private volatile transient long createTime;
     /*最后获取缓存时间*/
     private volatile transient long lastGetCacheTime;
     /*true 表示是滑动缓存*/
     private volatile transient boolean slide;
     /*清理时间*/
     private volatile transient long clearTime;

     public CacheBase() {
     }

     /**
      * 创建
      * @param slide
      * @param clearTime
      */
     protected CacheBase(boolean slide, long clearTime) {
         this.slide                      = slide;
         this.clearTime                  = clearTime;
     }

     /**
      *
      */
     protected void createCache(){
         this.edit                       = false;
         this.versionId                  = new AtomInteger(1);
         this.createTime                 = System.currentTimeMillis();
         this.lastGetCacheTime           = System.currentTimeMillis();
     }

         /**
      *
      */
     protected void copyCache(CacheBase tmp){
         this.edit                       = tmp.edit;
         this.versionId                  = tmp.getVersionId();
         this.createTime                 = tmp.getClearTime();
         this.lastGetCacheTime           = System.currentTimeMillis();
     }

     public boolean isEdit() {
         return edit;
     }

     public void setEdit(boolean edit) {
         this.edit = edit;
     }

     public AtomInteger getVersionId() {
         return versionId;
     }

     public void setVersionId(AtomInteger versionId) {
         this.versionId = versionId;
     }

     public long getCreateTime() {
         return createTime;
     }

     public void setCreateTime(long createTime) {
         this.createTime = createTime;
     }

     public long getLastGetCacheTime() {
         return lastGetCacheTime;
     }

     public void setLastGetCacheTime(long lastGetCacheTime) {
         this.lastGetCacheTime = lastGetCacheTime;
     }

     public boolean isSlide() {
         return slide;
     }

     public void setSlide(boolean slide) {
         this.slide = slide;
     }

     public long getClearTime() {
         return clearTime;
     }

     public void setClearTime(long clearTime) {
         this.clearTime = clearTime;
     }

 }

改造CacheManager类

 package net.sz.net.sz.framework.caches;

 import java.util.concurrent.ConcurrentHashMap;
 import net.sz.framework.szlog.SzLogger;
 import net.sz.framework.szthread.ThreadPool;

 /**
  *
  * <br>
  * author 失足程序员<br>
  * blog http://www.cnblogs.com/ty408/<br>
  * mail 492794628@qq.com<br>
  * phone 13882122019<br>
  */
 public class CacheManager {

     private static SzLogger log = SzLogger.getLogger();

     /*缓存集合*/
     final ConcurrentHashMap<String, CacheBase> cacheMap = new ConcurrentHashMap<>();

     public CacheManager() {
         /*创建一秒钟检查的定时器*/
         ThreadPool.addTimerTask(ThreadPool.GlobalThread, new CheckCacheTimer(1000, this));
     }

     /**
      * 默认30分钟清理的滑动缓存、如果存在缓存键、将不再添加
      *
      * @param key
      * @param object
      * @return
      */
     public boolean addCache(String key, CacheBase object) {
         return addCache(key, object, 30 * 60 * 1000);
     }

     /**
      * 默认滑动缓存、如果存在缓存键、将不再添加
      *
      * @param key
      * @param object
      * @param clearTime 滑动缓存的清理时间
      * @return
      */
     public boolean addCache(String key, CacheBase object, long clearTime) {
         return addCache(key, object, clearTime, true);
     }

     /**
      * 默认滑动缓存、如果存在缓存键、将不再添加
      *
      * @param key
      * @param object
      * @param clearTime 清理缓存的间隔时间
      * @param isSlide true表示滑动缓存,
      * @return
      */
     public boolean addCache(String key, CacheBase object, long clearTime, boolean isSlide) {
         return addCache(key, object, clearTime, isSlide, false);
     }

     /**
      *
      * @param key
      * @param object
      * @param clearTime 清理缓存的间隔时间
      * @param isSlide true表示滑动缓存,
      * @param put true 表示强制添加数据集合,及已经存在键数据
      * @return
      */
     public boolean addCache(String key, CacheBase object, long clearTime, boolean isSlide, boolean put) {
         if (put) {
             object.createCache();
             cacheMap.put(key, object);
             if (log.isDebugEnabled()) {
                 log.debug("强制添加缓存键=" + key);
             }
             return true;
         } else if (!cacheMap.containsKey(key)) {
             /*加入同步操作*/
             synchronized (key) {
                 if (!cacheMap.containsKey(key)) {
                     object.createCache();
                     cacheMap.put(key, object);
                     return true;
                 } else {
                     if (log.isDebugEnabled()) {
                         log.debug("数据已经添加,不能再次添加");
                     }
                 }
             }
         } else {
             if (log.isDebugEnabled()) {
                 log.debug("数据已经添加,不能再次添加");
             }
         }
         return false;
     }

     public boolean removeCache(String key) {
         cacheMap.remove(key);
         return true;
     }

     public CacheBase getCache(String key) {
         return getCache(key, CacheBase.class);
     }

     /**
      * 获取一条数据,这里我只是测试,提供思路,
      * <br>
      * 所以不会去考虑list等情况;
      * <br>
      * 需要的话可以自行修改
      *
      * @param <T>
      * @param clazz
      * @param key
      * @return
      */
     public <T extends CacheBase> T getCache(String key, Class<T> clazz) {
         CacheBase cacheBase = null;
         cacheBase = cacheMap.get(key);
         if (cacheBase != null) {
             /*更新最后获取缓存的时间*/
             cacheBase.setLastGetCacheTime(System.currentTimeMillis());
         }
         return (T) cacheBase;
     }

     /**
      * 更新缓存数据同时更新数据库数据
      *
      * @param key
      * @param object
      * @return
      */
     public boolean updateCacheBase(String key, CacheBase object) {
         if (object == null) {
             throw new UnsupportedOperationException("参数 object 为 null");
         }
         CacheBase cacheBase = null;
         cacheBase = cacheMap.get(key);
         /*理论上,控制得当这里是不可能为空的*/
         if (cacheBase != null) {
             /*理论上是能绝对同步的,你也可以稍加修改*/
             synchronized (key) {
                 /*验证编辑状态和版号,保证写入数据是绝对正确的*/
                 if (cacheBase.getVersionId() == object.getVersionId()) {
                     /*拷贝最新数据操作*/
                     cacheMap.put(key, object);
                     /*保证写入数据库后进行修改 对版本号进行加一操作*/
                     object.getVersionId().changeZero(1);
                     /*设置最新的最后访问时间*/
                     object.setLastGetCacheTime(System.currentTimeMillis());
                     /*修改编辑状态*/
                     object.setEdit(false);
                     if (log.isDebugEnabled()) {
                         log.debug("数据已修改,最新版号:" + object.getVersionId());
                     }
                     return true;
                 } else {
                     if (log.isDebugEnabled()) {
                         log.debug("版本已经修改无法进行更新操作");
                     }
                     throw new UnsupportedOperationException("版本已经修改无法进行更新操作");
                 }
             }
         } else {
             if (log.isDebugEnabled()) {
                 log.debug("缓存不存在无法修改数据");
             }
             throw new UnsupportedOperationException("缓存不存在无法修改数据");
         }
     }

     /**
      * 获取独占编辑状态
      *
      * @param key
      * @return
      */
     public boolean updateEdit(String key) {
         CacheBase cacheBase = null;
         cacheBase = cacheMap.get(key);
         if (cacheBase == null) {
             throw new UnsupportedOperationException("未找到数据源");
         }
         return updateEdit(key, cacheBase);
     }

     /**
      * 获取独占编辑状态
      *
      * @param key
      * @param cacheBase
      * @return
      */
     public boolean updateEdit(String key, CacheBase cacheBase) {
         if (cacheBase == null) {
             throw new UnsupportedOperationException("参数 cacheBase 为 null");
         }
         if (!cacheBase.isEdit()) {
             synchronized (key) {
                 if (!cacheBase.isEdit()) {
                     /*同步后依然需要双重判定*/
                     cacheBase.setEdit(true);
                     /*设置最新的最后访问时间*/
                     cacheBase.setLastGetCacheTime(System.currentTimeMillis());
                     return true;
                 }
             }
         }
         return false;
     }

 }

改造CheckCacheTimer类

 package net.sz.net.sz.framework.caches;

 import java.util.HashMap;
 import java.util.Map;
 import net.sz.framework.szlog.SzLogger;
 import net.sz.framework.szthread.TimerTaskModel;

 /**
  *
  * <br>
  * author 失足程序员<br>
  * blog http://www.cnblogs.com/ty408/<br>
  * mail 492794628@qq.com<br>
  * phone 13882122019<br>
  */
 public class CheckCacheTimer extends TimerTaskModel {

     private static SzLogger log = SzLogger.getLogger();
     private final CacheManager cacheManager;

     public CheckCacheTimer(int intervalTime, CacheManager cacheManager) {
         super(intervalTime);
         this.cacheManager = cacheManager;
     }

     @Override
     public void run() {
         /*考虑缓存的清理的都放在这里、当然有很多值的注意细节有待细化*/
         HashMap<String, CacheBase> tmp = new HashMap(this.cacheManager.cacheMap);
         for (Map.Entry<String, CacheBase> entry : tmp.entrySet()) {
             String key = entry.getKey();
             CacheBase value = entry.getValue();
             /*理论上,这里是能够保证绝对缓存,同步*/
             if (!value.isEdit()) {
                 /*滑动缓存清理*/
                 if (value.isSlide() && System.currentTimeMillis() - value.getLastGetCacheTime() < value.getClearTime()) {
                     continue;
                 }
                 /*固定缓存清理*/
                 if (!value.isSlide() && System.currentTimeMillis() - value.getCreateTime() < value.getClearTime()) {
                     continue;
                 }
                 this.cacheManager.removeCache(key);
             }
         }
     }
 }

脱离了数据源的缓存器;

求大神指教了;如果觉得可以点个推荐;

觉得不好请手下留情不要点击反对哦,

最新文章

  1. OpenCASCADE Conic to BSpline Curves-Hyperbola
  2. 新手之自动转存DLL——20150626星期五
  3. Endless Sky源码学习笔记-2
  4. Scrum三大角色特点
  5. 一种快速刷新richedit中内嵌动画的方法的实现
  6. CodeForceS#276-B(求最大值)
  7. Linq之Linq to Sql
  8. PHP代码优化的53个细节
  9. Putty终端 模拟 远程登录 虚拟机Linux
  10. SharePoint 2010 母版页制作的简单介绍
  11. SufaceView(绘图类)
  12. 关于struts2中的default-action-ref
  13. 老李分享:Android -自动化埋点 3
  14. 没事不要在for循环期间增减迭代序列的成员
  15. python3+requests库框架设计06-测试脚本
  16. JMeter性能测试基础 (4)-使用JMeter录制测试脚本
  17. SQL Server 2012 R2升级到SQL Server 2016无法正常启动数据库服务解决方案
  18. FCC JS基础算法题(7):Chunky Monkey(分割数组)
  19. StarRatingBar星星切换动画《IT蓝豹》
  20. 激活office软件

热门文章

  1. android学习2——RelativeLayout
  2. Activiti工作流(一)之基本操作介绍
  3. AR入门系列-03-在unity中将调试好的Vuforia项目导出为APK
  4. 读书笔记 effective c++ Item 30 理解内联的里里外外 (大师入场啦)
  5. 使用postfix在debian上配置邮件服务器
  6. mysql数据库开始——增删改
  7. koahub软件市场微信编辑器源码,可下载
  8. Codevs3278[NOIP2013]货车运输
  9. angularjs fileUpload
  10. WPF中MeasureOverride ArrangeOverride 的理解