客户有新需求,需要修改数据库、配置文件等,可以通过打补丁的方式来处理:
使用方法:
1、创建一个类继承ApplicationRunner,重写run方法;
2、将这个类丢给容器,写法如下:
@Component public class ApplicationRunnerImpl implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { } }
SpringBoot自动会运行run方法,至于是通过反射查到该对象,还是从容器里拿到对象集在一个个instanceof判断,或者其他方法获取对象,无所谓。只要实现ApplicationRunner,再通过注解注入容器中就行。
例子:数据库某个字段设计时长度设置500,客户使用一段时间后发现不够用了,需要修改成2000,这个时候最快的解决方案可以是现场,也可以远程直接打开数据库修改就好了。可如果数据库已经使用了很长一段时间,里面有机密数据,客户拒接提供数据库,这个时候只能在代码里改,改完发给客户一个新版本就好。要改的话在最简单的方式是在启动类里改,使用补丁是为了统一管理,后期代码好找(维护)。
补丁类:
@Component public class PatchDataSecond extends PatchBase { @Autowired EntityManager entityManager; public PatchDataSecond(EntityManager entityManager) { super(entityManager); } @Override public boolean run() { try { String sql = "ALTER TABLE bm_recovery MODIFY COLUMN record varchar(2000)"; entityManager.createNativeQuery(sql).executeUpdate(); } catch (Exception e) { e.printStackTrace(); return false; } return true; } }
ApplicationRunner实现类:
/** * 在app运行成功后执行一些特定操作,一些特殊的任务。例如初始化参数、数据库初始化等操作 */ @Component @Transactional public class ApplicationRunnerImpl implements ApplicationRunner { String TAG = "ApplicationRunnerImpl"; /** * @PersistenceContext 注入的是实体管理器,执行持久化操作。 * EntityManager 实体管理器 */ @PersistenceContext private EntityManager entityManager; @Autowired PatchDataSecond patchDataSecond; @Override public void run(ApplicationArguments args) throws Exception { patchDataSecond.run(); } }
如果想方便的话可以在补丁类和ApplicationRunner实现类中间套一层,加一个补丁管理类,补丁管理类里维护一个list集合,每有一个新补丁修改补丁管理类的代码,将新补丁加入list,所有补丁添加结束后,可以for循环拿出来一个个顺序执行补丁的run方法。而ApplicationRunner实现类只要执行补丁管理类的run方法就好。
补丁管理类:
@Component public class PatchManage { final static String TAG = "PatchManage"; EntityManager entityManager; List<PatchBase> patchList = new ArrayList<>(); @Autowired PatchDataSecond patchDataSecond; public void init(EntityManager entityManager) { this.entityManager = entityManager; patchDataSecond.setEntityManager(entityManager); //添加补丁 patchList.add(patchDataSecond); } /** * ApplicationRunner实现类可以把entityManger传递过来 */ public void run(EntityManager entityManager) { init(entityManager); for (PatchBase patch : patchList) { patch.run(); } } }
完整的补丁管理类:
/** * 补丁管理类 * @author java */ @Component public class PatchManage { /** * 实体管理类 */ EntityManager entityManager; /** * 补丁集合 */ List<PatchBase> patchList = new ArrayList<>(); /** * 补丁对象,实现PatchBase接口 */ private final PatchInitDbData patchInitDbData; private final PatchDataSecond patchDataSecond; /** * 通过构造方法注入对象,可以避过阿里巴巴的代码规范,代码规范不推荐使用@Autowired注入对象 */ public PatchManage(PatchInitDbData patchInitDbData, PatchDataSecond patchDataSecond) { this.patchInitDbData = patchInitDbData; this.patchDataSecond = patchDataSecond; } /** * 业务管理类的初始化,新补丁在这里加入 * @param entityManager 实体管理对象,找SpringBoot要 */ public void init(EntityManager entityManager) { //将实体管理对象给到补丁,如果用不上也可以不给 this.entityManager = entityManager; patchInitDbData.setEntityManager(entityManager); patchDataSecond.setEntityManager(entityManager); //加入list集合 patchList.add(patchInitDbData); patchList.add(patchDataSecond); } /** * 补丁执行类Unchecked assignment: 'java.util.List' to 'java.util.List<com.brain.machine.entity.PatchVersion>' */ public void run() { //在数据库里加一张补丁管理表,管理补丁,已经执行过的补丁不再执行,这在设计之初应该设计进去 String sql = "select * from patch_version"; Query query = entityManager.createNativeQuery(sql, PatchVersion.class); List<PatchVersion> versions = query.getResultList(); //循环遍历补丁 for (PatchBase patch : patchList) { //从数据库获取补丁的运行态 String status = getPatchStatus(versions, patch.name()); //如果为null,代表这是新加的补丁,写入数据库 if ("null".equals(status)) { insertNewPatch(patch); } //如果是end或者run,代表执行过的补丁,结束当前循环(end是在补丁结束的时候打上的,这个run在哪打上的没找到) if ("end".equals(status) || "run".equals(status)) { continue; } //新补丁执行run方法,如果执行成功,在数据库里留下痕迹,告知已经执行过了 if (patch.run()) { endPatch(patch); } } } /** * 获取补丁状态,versions是从数据库查到的数据,检查补丁的运行状态,查不到就返回null */ public String getPatchStatus(List<PatchVersion> versions, String name) { //循环遍历 for (PatchVersion version : versions) { //名称相同代表同一个补丁,返回运行状态(这里好像没做补丁同名处理,数据库也没有...) if (version.name.equals(name)) { return version.status; } } return "null"; } /** * 添加补丁,如果补丁第一次执行,在数据库里记录下来 */ public void insertNewPatch(PatchBase patch) { //加入数据库 String newSql = "insert into patch_version(name, status, create_time) values(?, ?, ?)"; Query query = entityManager.createNativeQuery(newSql) .setParameter(1, patch.name()) //ready准备状态 .setParameter(2, "ready") .setParameter(3, new Date()); query.executeUpdate(); } /** * 补丁执行结束后,修改自己的状态。这样下一次启动项目,会跳过该补丁(注意:补丁执行一次就不会再执行,如果写错了,要么改名,要么重写一个类) * 禁用 */ public void endPatch(PatchBase patch) { String updateSql = "update patch_version set status = 'end', end_time = ? where name = ? "; Query query = entityManager.createNativeQuery(updateSql) .setParameter(1, new Date()) .setParameter(2, patch.name()); query.executeUpdate(); } }
浙公网安备 33010602011771号