移动架构-数据库分库和全版本升级

在项目中,往往涉及到数据库的版本升级,通常会有两种升级方式,一种是纯代码实现,一种是脚本实现,这里使用脚本升级,这样的升级方式更便于维护

思路

全版本升级,重点在于数据的迁移,这里使用备份原数据库,新建数据库,增删字段,复制数据库的升级思路,其版本控制在脚本中声明,代码在写好之后基本是不会变动的,更利于维护

SQLite修改表语句

  • 创建表 CREATE TABLE 表名 (列名 数据类型 限定符...)
CREATE TABLE Table (ID INTEGER,NAME TEXT);
  • 修改表 ALTER TABLE ...
    命令允许用户重命名或添加新的字段在已有表中,不能从表中删除字段。并且只能在表的末尾添加字段
ALTER TABLE tTable RENAME TO MyTable;
  • 添加一列 ALTER TABLE 表名 ADD COLUMN 列名 数据类型 限定符
ALTER TABLE MyTable ADD COLUMN AGE INTEGER;
  • 删除表 DROP TABLE 表名
DROP TABLE MyTable;

更改表结构的方法:

  1. 当表中没有任何数据时
    删除表
DROP TABLE MyTable;

创建表

CREATE TABLE MyTable ...
  1. 当表中有数据时
    将表名改为临时表
ALTER TABLE MyTable RENAME TO _temp_MyTable;

创建新表

CREATE TABLE MyTable (....);

导入数据

INSERT INTO MyTable SELECT .., .. ,"用空来补充原来不存在的数据" FROM _temp_MyTable;

删除临时表

DROP TABLE _temp_MyTable;

实现

这里沿用设计的数据库,数据库分库已经在项目中包含了
修改BaseDaoFactory,使其支持数据库分库

public class BaseDaoFactory {private static final String TAG = "BaseDaoFactory";private String sqliteDatabasePath;private SQLiteDatabase sqLiteDatabase;private SQLiteDatabase userDatabase;private Map<String, BaseDao> map = Collections.synchronizedMap(new HashMap<String, BaseDao>());private static BaseDaoFactory instance = new BaseDaoFactory();public static BaseDaoFactory getInstance() {return instance;}private BaseDaoFactory() {File file = new File(Environment.getExternalStorageDirectory(), "update");if (!file.exists()) {file.mkdirs();}sqliteDatabasePath = file.getAbsolutePath() + "/user.db";openDatabase();}public synchronized <T extends BaseDao<M>, M> T getDataHelper(Class<T> clazz, Class<M> entityClass) {BaseDao baseDao = null;if (map.get(clazz.getSimpleName()) != null) {return (T) map.get(clazz.getSimpleName());}try {baseDao = clazz.newInstance();baseDao.init(entityClass, sqLiteDatabase);map.put(clazz.getSimpleName(), baseDao);} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return (T) baseDao;}public synchronized <T extends BaseDao<M>, M> T getUserHelper(Class<T> clazz, Class<M> entityClass) {openUserDatabase(getPath());BaseDao baseDao = null;//反射得到对象类型try {baseDao = clazz.newInstance();baseDao.init(entityClass, userDatabase);map.put(clazz.getSimpleName(), baseDao);} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return (T) baseDao;}private void openDatabase() {//打开数据库,如果不存在则创建sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqliteDatabasePath, null);}public String getPath() {UserDao userDao = BaseDaoFactory.getInstance().getDataHelper(UserDao.class, User.class);if (userDao == null) {return null;}User currentUser = userDao.getCurrentUser();if (currentUser == null) {return null;}File file = new File(Environment.getExternalStorageDirectory(), "update");if (!file.exists()) {file.mkdirs();}File childFile = new File(file.getAbsolutePath(), currentUser.getUser_id());if(!childFile.exists()){childFile.mkdirs();}return file.getAbsolutePath() + "/" + currentUser.getUser_id() + "/logic.db";
}private void openUserDatabase(String userDBPath) {//打开数据库,如果不存在则创建userDatabase = SQLiteDatabase.openOrCreateDatabase(userDBPath, null);}
}

创建数据库脚本

public class CreateDb {//数据库表名private String name;//创建表的sql语句集合private List<String> sqlCreates;public CreateDb(Element ele) {name = ele.getAttribute("name");sqlCreates = new ArrayList<String>();NodeList sqls = ele.getElementsByTagName("sql_createTable");for (int i = 0; i < sqls.getLength(); i++) {String sqlCreate = sqls.item(i).getTextContent();this.sqlCreates.add(sqlCreate);}}public String getName() {return name;}public void setName(String name) {this.name = name;}public List<String> getSqlCreates() {return sqlCreates;}public void setSqlCreates(List<String> sqlCreates) {this.sqlCreates = sqlCreates;}
}

数据库升级创建表脚本

public class CreateVersion {//版本信息private String version;//创建数据库表脚本private List<CreateDb> createDbs;public CreateVersion(Element ele) {version = ele.getAttribute("version");createDbs = new ArrayList<CreateDb>();NodeList cs = ele.getElementsByTagName("createDb");for (int i = 0; i < cs.getLength(); i++) {Element ci = (Element) (cs.item(i));CreateDb cd = new CreateDb(ci);this.createDbs.add(cd);}}public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}public List<CreateDb> getCreateDbs() {return createDbs;}public void setCreateDbs(List<CreateDb> createDbs) {this.createDbs = createDbs;}
}

复制单个文件(可更名复制)

public class FileUtil {public static void CopySingleFile(String oldPathFile, String newPathFile) {try {int bytesum = 0;int byteread = 0;File oldfile = new File(oldPathFile);File newFile = new File(newPathFile);File parentFile = newFile.getParentFile();if (!parentFile.exists()) {parentFile.mkdirs();}//文件存在时if (oldfile.exists()) {InputStream inStream = new FileInputStream(oldPathFile);FileOutputStream fs = new FileOutputStream(newPathFile);byte[] buffer = new byte[1024];while ((byteread = inStream.read(buffer)) != -1) {bytesum += byteread; //字节数 文件大小fs.write(buffer, 0, byteread);}inStream.close();}} catch (Exception e) {e.printStackTrace();}}
}

更新数据库脚本

public class UpdateDb {//数据库名称private String dbName;private List<String> sqlBefores;private List<String> sqlAfters;public UpdateDb(Element ele) {dbName = ele.getAttribute("name");sqlBefores = new ArrayList<String>();sqlAfters = new ArrayList<String>();NodeList sqlsBefore = ele.getElementsByTagName("sql_before");for (int i = 0; i < sqlsBefore.getLength(); i++) {String sql_before = sqlsBefore.item(i).getTextContent();this.sqlBefores.add(sql_before);}NodeList sqlsAfter = ele.getElementsByTagName("sql_after");for (int i = 0; i < sqlsAfter.getLength(); i++) {String sql_after = sqlsAfter.item(i).getTextContent();this.sqlAfters.add(sql_after);}}public String getDbName() {return dbName;}public void setDbName(String dbName) {this.dbName = dbName;}public List<String> getSqlBefores() {return sqlBefores;}public void setSqlBefores(List<String> sqlBefores) {this.sqlBefores = sqlBefores;}public List<String> getSqlAfters() {return sqlAfters;}public void setSqlAfters(List<String> sqlAfters) {this.sqlAfters = sqlAfters;}
}

升级更新数据库

public class UpdateDbXml {//升级脚本列表private List<UpdateStep> updateSteps;//升级版本private List<CreateVersion> createVersions;public UpdateDbXml(Document document) {// 获取升级脚本NodeList updateSteps = document.getElementsByTagName("updateStep");this.updateSteps = new ArrayList<UpdateStep>();for (int i = 0; i < updateSteps.getLength(); i++) {Element ele = (Element) (updateSteps.item(i));UpdateStep step = new UpdateStep(ele);this.updateSteps.add(step);}//获取各升级版本NodeList createVersions = document.getElementsByTagName("createVersion");this.createVersions = new ArrayList<CreateVersion>();for (int i = 0; i < createVersions.getLength(); i++) {Element ele = (Element) (createVersions.item(i));CreateVersion cv = new CreateVersion(ele);this.createVersions.add(cv);}}public List<UpdateStep> getUpdateSteps() {return updateSteps;}public void setUpdateSteps(List<UpdateStep> updateSteps) {this.updateSteps = updateSteps;}public List<CreateVersion> getCreateVersions() {return createVersions;}public void setCreateVersions(List<CreateVersion> createVersions) {this.createVersions = createVersions;}
}

数据库升级脚本信息

public class UpdateStep {//旧版本private String versionFrom;//新版本private String versionTo;//更新数据库脚本private List<UpdateDb> updateDbs;public UpdateStep(Element ele) {versionFrom = ele.getAttribute("versionFrom");versionTo = ele.getAttribute("versionTo");updateDbs = new ArrayList<UpdateDb>();NodeList dbs = ele.getElementsByTagName("updateDb");for (int i = 0; i < dbs.getLength(); i++) {Element db = (Element) (dbs.item(i));UpdateDb updateDb = new UpdateDb(db);this.updateDbs.add(updateDb);}}public List<UpdateDb> getUpdateDbs() {return updateDbs;}public void setUpdateDbs(List<UpdateDb> updateDbs) {this.updateDbs = updateDbs;}public String getVersionFrom() {return versionFrom;}public void setVersionFrom(String versionFrom) {this.versionFrom = versionFrom;}public String getVersionTo() {return versionTo;}public void setVersionTo(String versionTo) {this.versionTo = versionTo;}
}

数据库脚本管理

public class UpdateManager {private static final String TAG = "UpdateManager";private static final String INFO_FILE_DIV = "/";private List<User> userList;private File parentFile = new File(Environment.getExternalStorageDirectory(), "update");private File bakFile = new File(parentFile, "backDb");public UpdateManager() {if (!parentFile.exists()) {parentFile.mkdirs();}if (!bakFile.exists()) {bakFile.mkdirs();}}public void checkThisVersionTable(Context context) {UserDao userDao = BaseDaoFactory.getInstance().getDataHelper(UserDao.class, User.class);userList = userDao.query(new User());UpdateDbXml xml = readDbXml(context);String thisVersion = getVersionName(context);CreateVersion thisCreateVersion = analyseCreateVersion(xml, thisVersion);try {executeCreateVersion(thisCreateVersion, true);} catch (Exception e) {}}//开始升级public void startUpdateDb(Context context) {UpdateDbXml updateDbxml = readDbXml(context);if (getLocalVersionInfo()) {//拿到当前版本String thisVersion = getVersionName(context);//拿到上一个版本String lastVersion = lastBackupVersion;UpdateStep updateStep = analyseUpdateStep(updateDbxml, lastVersion, thisVersion);if (updateStep == null) {return;}List<UpdateDb> updateDbs = updateStep.getUpdateDbs();CreateVersion createVersion = analyseCreateVersion(updateDbxml, thisVersion);try {//更新每个用户的数据库for (User user : userList) {String logicDbDir = parentFile.getAbsolutePath() + "/update" + "/" + user.getUser_id() + "/logic.db";String logicCopy = bakFile.getAbsolutePath() + "/" + user.getUser_id() + "/logic.db";FileUtil.CopySingleFile(logicDbDir, logicCopy);}//备份总数据库String user = parentFile.getAbsolutePath() + "/user.db";String user_bak = bakFile.getAbsolutePath() + "/user.db";FileUtil.CopySingleFile(user, user_bak);// 第二步:执行sql_before语句,删除以及备份相关旧表executeDb(updateDbs, -1);// 第三步:检查新表,创建新表executeCreateVersion(createVersion, false);Log.d(TAG, "第三步检查新表完成!");// 第四步:从备份表中恢复数据,恢复后删除备份表executeDb(updateDbs, 1);} catch (Exception e) {e.printStackTrace();}// 第五步:升级成功,删除备份数据库if (userList != null && !userList.isEmpty()) {for (User user : userList) {String logicDbDir = parentFile.getAbsolutePath() + "/update" + "/" + user.getUser_id() + ".db";File file = new File(logicDbDir);if (file.exists()) {file.delete();}}}File userFileBak = new File(bakFile.getAbsolutePath() + "user_bak.db");if (userFileBak.exists()) {userFileBak.delete();}Log.d(TAG, "升级成功");}}//根据建表脚本,核实一遍应该存在的表private void executeCreateVersion(CreateVersion createVersion, boolean isLogic) throws Exception {if (createVersion == null || createVersion.getCreateDbs() == null) {throw new Exception("createVersion or createDbs is null;");}for (CreateDb cd : createVersion.getCreateDbs()) {if (cd == null || cd.getName() == null) {throw new Exception("db or dbName is null when createVersion;");}if (!"logic".equals(cd.getName())) {continue;}// 创建数据库表sqlList<String> sqls = cd.getSqlCreates();SQLiteDatabase sqlitedb = null;try {// 逻辑层数据库要做多用户升级if (userList != null && !userList.isEmpty()) {// 多用户建新表for (int i = 0; i < userList.size(); i++) {// 获取dbsqlitedb = getDb(cd, userList.get(i).getUser_id());executeSql(sqlitedb, sqls);sqlitedb.close();}}} catch (Exception e) {e.printStackTrace();} finally {// 关闭数据库if (sqlitedb != null) {sqlitedb.close();}}}}//执行针对db升级的sql集合 type:小于0为建表前,大于0为建表后private void executeDb(List<UpdateDb> updateDbs, int type) throws Exception {if (updateDbs == null) {throw new Exception("updateDbs is null;");}for (UpdateDb db : updateDbs) {if (db == null || db.getDbName() == null) {throw new Exception("db or dbName is null;");}List<String> sqls = null;//更改表if (type < 0) {sqls = db.getSqlBefores();} else if (type > 0) {sqls = db.getSqlAfters();}SQLiteDatabase sqlitedb = null;try {// 逻辑层数据库要做多用户升级if (userList != null && !userList.isEmpty()) {// 多用户表升级for (int i = 0; i < userList.size(); i++) {sqlitedb = getDb(db, userList.get(i).getUser_id());executeSql(sqlitedb, sqls);sqlitedb.close();}}} catch (Exception e) {e.printStackTrace();} finally {if (null != sqlitedb) {sqlitedb.close();}}}}//执行sql语句private void executeSql(SQLiteDatabase sqlitedb, List<String> sqls) throws Exception {// 检查参数if (sqls == null || sqls.size() == 0) {return;}// 事务sqlitedb.beginTransaction();for (String sql : sqls) {sql = sql.replaceAll("\r\n", " ");sql = sql.replaceAll("\n", " ");if (!"".equals(sql.trim())) {try {// Logger.i(TAG, "执行sql:" + sql, false);sqlitedb.execSQL(sql);} catch (SQLException e) {}}}sqlitedb.setTransactionSuccessful();sqlitedb.endTransaction();}//新表插入数据private UpdateStep analyseUpdateStep(UpdateDbXml xml, String lastVersion, String thisVersion) {if (lastVersion == null || thisVersion == null) {return null;}// 更新脚本UpdateStep thisStep = null;if (xml == null) {return null;}List<UpdateStep> steps = xml.getUpdateSteps();if (steps == null || steps.size() == 0) {return null;}for (UpdateStep step : steps) {if (step.getVersionFrom() == null || step.getVersionTo() == null) {} else {// 升级来源以逗号分隔String[] lastVersionArray = step.getVersionFrom().split(",");if (lastVersionArray != null && lastVersionArray.length > 0) {for (int i = 0; i < lastVersionArray.length; i++) {// 有一个配到update节点即升级数据if (lastVersion.equalsIgnoreCase(lastVersionArray[i]) && step.getVersionTo().equalsIgnoreCase(thisVersion)) {thisStep = step;break;}}}}}return thisStep;}//根据xml对象获取对应要修改的db文件private SQLiteDatabase getDb(UpdateDb db, String userId) {return getDb(db.getDbName(), userId);}private SQLiteDatabase getDb(CreateDb db, String userId) {return getDb(db.getName(), userId);}//创建数据库,获取数据库对应的SQLiteDatabaseprivate SQLiteDatabase getDb(String dbname, String userId) {String dbfilepath = null;SQLiteDatabase sqlitedb = null;File file = new File(parentFile, userId);if (!file.exists()) {file.mkdirs();}if (dbname.equalsIgnoreCase("logic")) {dbfilepath = file.getAbsolutePath() + "/logic.db";// logic对应的数据库路径} else if (dbname.equalsIgnoreCase("user")) {dbfilepath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/user.db";// service对应的数据库}if (dbfilepath != null) {File f = new File(dbfilepath);f.mkdirs();if (f.isDirectory()) {f.delete();}sqlitedb = SQLiteDatabase.openOrCreateDatabase(dbfilepath, null);}return sqlitedb;}//解析出对应版本的建表脚本private CreateVersion analyseCreateVersion(UpdateDbXml xml, String version) {CreateVersion cv = null;if (xml == null || version == null) {return cv;}List<CreateVersion> createVersions = xml.getCreateVersions();if (createVersions != null) {for (CreateVersion item : createVersions) {// 如果表相同则要支持xml中逗号分隔String[] createVersion = item.getVersion().trim().split(",");for (int i = 0; i < createVersion.length; i++) {if (createVersion[i].trim().equalsIgnoreCase(version)) {cv = item;break;}}}}return cv;}//读取升级xmlprivate UpdateDbXml readDbXml(Context context) {InputStream is = null;Document document = null;try {is = context.getAssets().open("updateXml.xml");DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();document = builder.parse(is);} catch (Exception e) {e.printStackTrace();} finally {if (is != null) {try {is.close();} catch (IOException e) {e.printStackTrace();}}}if (document == null) {return null;}UpdateDbXml xml = new UpdateDbXml(document);return xml;}//获取APK版本号public String getVersionName(Context context) {String versionName = null;try {PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);versionName = info.versionName;} catch (PackageManager.NameNotFoundException e) {}return versionName;}//保存下载APK版本信息public boolean saveVersionInfo(Context context, String newVersion) {boolean ret = false;FileWriter writer = null;try {writer = new FileWriter(new File(parentFile, "update.txt"), false);writer.write("V003" + INFO_FILE_DIV + "V002");writer.flush();ret = true;} catch (IOException e) {} finally {if (writer != null) {try {writer.close();} catch (IOException e) {e.printStackTrace();}}}return ret;}//获取本地版本相关信息private String existVersion;private String lastBackupVersion;private boolean getLocalVersionInfo() {boolean ret = false;File file = new File(parentFile, "update.txt");if (file.exists()) {int byteread = 0;byte[] tempbytes = new byte[100];StringBuilder stringBuilder = new StringBuilder();InputStream in = null;try {in = new FileInputStream(file);while ((byteread = in.read(tempbytes)) != -1) {stringBuilder.append(new String(tempbytes, 0, byteread));}String[] infos = stringBuilder.toString().split(INFO_FILE_DIV);if (infos.length == 2) {existVersion = infos[0];lastBackupVersion = infos[1];ret = true;}} catch (Exception e) {} finally {if (null != in) {try {in.close();} catch (IOException e) {e.printStackTrace();}in = null;}}}return ret;}
}

模拟用户登录

@DbTable("tb_user")
public class User {public String name;public String password;public String user_id;public Integer status;public Integer getStatus() {return status;}public void setStatus(Integer status) {this.status = status;}public String getUser_id() {return user_id;}public void setUser_id(String user_id) {this.user_id = user_id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}
public class UserDao extends BaseDao<User> {private static final String TAG = "UserDao";@Overridepublic String createTable() {return "create table if not exists tb_user( name TEXT, password TEXT, user_id Text,status Integer);";}@Overridepublic long insert(User entity) {List<User> list = query(new User());User where = null;for (User user : list) {where = new User();where.setUser_id(user.getUser_id());user.setStatus(0);update(user, where);}Log.d(TAG, "用户" + entity.getName() + "登录");entity.setStatus(1);return super.insert(entity);}//得到当前登录的Userpublic User getCurrentUser() {User user = new User();user.setStatus(1);List<User> list = query(user);if (list.size() > 0) {return list.get(0);}return null;}
}

模拟用户下载数据

@DbTable("tb_photo")
public class Photo {public String time;public String path;public String getTime() {return time;}public void setTime(String time) {this.time = time;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}
}
public class PhotoDao extends BaseDao<Photo> {@Overridepublic String createTable() {return "create table if not exists tb_photo(\n" +"                time TEXT,\n" +"                path TEXT,\n" +"                to_user TEXT\n" +"                )";}
}

测试

public class MainActivity extends AppCompatActivity {UpdateManager updateManager;UserDao baseDao;int i = 0;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);updateManager = new UpdateManager();baseDao = BaseDaoFactory.getInstance().getDataHelper(UserDao.class, User.class);}public void login(View view) {User user = new User();user.setName("V00" + (i++));user.setPassword("123456");user.setName("jack" + i);user.setUser_id("N000" + i);baseDao.insert(user);updateManager.checkThisVersionTable(this);}public void insert(View view) {Photo photo = new Photo();photo.setPath("data/data/my.jpg");SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");photo.setTime(dateFormat.format(new Date()));PhotoDao photoDao = BaseDaoFactory.getInstance().getUserHelper(PhotoDao.class, Photo.class);photoDao.insert(photo);}public void write(View view) {//写入版本updateManager.saveVersionInfo(this, "V002");}public void update(View view) {updateManager.checkThisVersionTable(this);updateManager.startUpdateDb(this);}
}

在Assets文件夹加入升级脚本

<!-- 请保证该文档一定是 UTF-8编码 -->
<updateXml><createVersion version="V003"><createDb name="user"><!-- 设备与软件关联信息 --><sql_createTable>create table if not exists tb_user(name TEXT,password TEXT,loginName TEXT,lastLoginTime,user_id Integer primary key);</sql_createTable></createDb><createDb name="logic"><!-- 设备与软件关联信息 --><sql_createTable>create table if not exists tb_photo(time TEXT,path TEXT,to_user TEXT,sendTime TEXT);</sql_createTable></createDb></createVersion><updateStepversionFrom="V002"versionTo="V003"><updateDb name="logic"><sql_before>alter table tb_photo rename to bak_tb_photo;</sql_before><sql_after>insert into tb_photo(time,path)select time,pathfrom bak_tb_photo;</sql_after><sql_after>drop table if exists bak_tb_photo;</sql_after></updateDb><updateDb name="user"><sql_before>alter table tb_user rename to bak_t_user;</sql_before><sql_after>insert into tb_user(name,password)select name,passwordfrom bak_tb_user;</sql_after><sql_after>drop table if exists bak_t_user;</sql_after></updateDb></updateStep>
</updateXml>

注意当前版本与升级脚本的一致

转载于:https://www.cnblogs.com/cj5785/p/10664611.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/364962.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java中的硬件事务性内存,或者为什么同步将再次变得很棒

总览 硬件事务内存有潜力允许多个线程同时以推测方式访问相同的数据结构&#xff0c;并使缓存一致性协议确定是否发生冲突。 HTM旨在为您提供细粒度锁定的可伸缩性&#xff0c;粗粒度锁定的简单性以及几乎没有锁定的性能。 如果JVM支持&#xff0c;则您的程序或库是使用过程粒度…

mysql5.7乱码_mysql5.7中解决中文乱码的问题

在使用mysql5.7时&#xff0c;会发现通过web端向数据库中写入中文后会出现乱码&#xff0c;但是在数据库中直接操作SQL语句插入数据后中文就显示正常&#xff0c;这个问题怎么解决呢&#xff1f;此处不对mysql的数据编码过程和原理进行讲解&#xff0c;如果有兴趣的同学可以自己…

a 链接点击下载

1. 将链接设置为.zip 结尾2.在a元素中添加download 属性,&#xff08;目前只有chrome、firefox和opera支持&#xff09; function download(src) { var $a $("<a></a>").attr("href", src).attr("download", "img.png");…

Codeforces Global Round 2 D. Frets On Fire (动态开点线段树,沙雕写法)

题目链接&#xff1a;D. Frets On Fire 思路&#xff1a;明明可以离散化二分写&#xff0c;思路硬是歪到了线段树上&#xff0c;自闭了&#xff0c;真实弟弟&#xff0c;怪不得其他人过得那么快 只和查询的区间长度有关系&#xff0c;排完序如果相邻的两个点的差值小于等于查询…

hdu 1069 Monkey and Banana 【动态规划】

题目 题意&#xff1a;研究人员要测试猴子的IQ&#xff0c;将香蕉挂到一定高度&#xff0c;给猴子一些不同大小的箱子&#xff0c;箱子数量不限&#xff0c;让猩猩通过叠长方体来够到香蕉。 现在给你N种长方体&#xff0c; 要求&#xff1a;位于上面的长方体的长和宽 要小于 …

使用带有OAuth2的Web应用程序和使用JWT来调用API – WSO2 API Manager

在这篇文章中&#xff0c;我将分享我在WSO2 API管理器&#xff08;API-M&#xff09;方面的经验和理解&#xff0c;以解决该行业中非常普遍和有用的情况。 简要地说明以下流程。 公开了一个API&#xff0c;供应用程序开发人员在API Manager&#xff08;可为API添加访问控制&a…

git仓库的推送问题

git仓库的推送问题 推送问题 先说结论。先拉取&#xff0c;pull拉取完之后再commi提交。 如果先提交&#xff0c;再拉取的话&#xff0c;git会提示项目冲突&#xff0c;此时需要先解决冲突&#xff0c;而某些时候&#xff0c;我们不一定会处理好所有的冲突&#xff0c;而导致…

scala的插值器

Scala 为我们提供了三种字符串插值的方式&#xff0c;分别是 s, f 和 raw。它们都是定义在 StringContext 中的方法。 s 字符串插值器 val a 2println(s"小明今年$a 岁了") f 字符串插值器 它除 s 的功能外(不指定格式就和 s 一样)&#xff0c;还能进行格式化输出&a…

src/main/resorces applicationContext.xml

<?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:context"http://www.springframework.org/schema/context"xmlns:aop"http://www.springframework.org/schem…

layui的checkbox示例

1.html页面: var isSkipcheckbox ;if (appOptions.isSkip ! "0") {isSkipcheckbox checked"";}<input type"checkbox" name"" lay-filter"type" lay-skin"primary" title"是否允许跳过" value&quo…

confluence 编辑器加载_Onlyoffice集成Confluence的工作原理

onlyoffice 与 confluence集成使用&#xff0c;使用onlyoffice connector for confluence 插件对接confluence插件在confluence中创建了一个新的按钮动作&#xff0c;Edit in OnlyOffice 用来处理office文档。这允许多用户实时协作并且将变更返回到confluence中。具体工作原理&…

红帽JBoss BRMS和BPMS富客户端框架展示了与GWT / Errai / UberFire和AngularJS的多语言集成...

上周&#xff0c;我发布了一个博客&#xff0c;重点介绍了我的演示文稿&#xff0c;该演示文稿展示了我们在BRMS和BPMS平台内完成的工作&#xff0c;Drools和jBPM项目的产品化版本所产生的丰富客户端平台。 该演示文稿是所有屏幕截图和视频&#xff0c;您可以在此处找到博客和幻…

AJAX的内容

第 1 章&#xff1a;原生 AJAX 1.1 AJAX 简介 AJAX 全称为 Asynchronous JavaScript And XML&#xff0c;就是异步的 JS 和 XML。 通过 AJAX 可以在浏览器中向服务器发送异步请求&#xff0c;最大的优势&#xff1a;无刷新获取数据。 AJAX 不是新的编程语言&#xff0c;而是…

洛谷P1099 树网的核

传送门 80分 $ Floyd $ 树的直径可以通过枚举求出。直径的两个端点$ maxi,maxj $ &#xff0c;由此可知对于一个点 $ k $ &#xff0c;如果满足 $ d[maxi][k]d[k][maxj]d[maxi][maxj] $ 那么 $ k $ 点一定在直径上。分别枚举位于直径上的起点 $ s $ 与终点 $ t $ 。 $ ecg $ 定…

HTML中直接写js 函数

1.在HTML中直接写JS函数: <body οnlοad"javascript:{window.location.hrefhttp://www.baidu.com/}">2.写在script标签里的话就是&#xff1a; <script>function tiao(){window.location.href"http://www.baidu.com/"}</script><!--…

选择通过更改内容类型返回的详细程度,第二部分

在上一篇文章中 &#xff0c;我们研究了使用MOXy的功能来控制特定实体的数据输出级别。 这篇文章着眼于Jersey 2.x提供的抽象&#xff0c;它允许您定义一组自定义的批注以具有相同的效果。 与之前一样&#xff0c;我们几乎没有什么琐碎的资源可以返回Jersey将为我们转换为JSON…

jQuery.ajax success 与 complete 区别

作者QQ&#xff1a;1095737364 QQ群&#xff1a;123300273 欢迎加入&#xff01; 天天用,不知所以然: $.ajax({type: "post",url: url,dataType:html,success: function(data) { },complete: function(XMLHttpRequest, textStatus) { },error: function(){}}…

vscode如何设置回车自动换行缩进?

要解决这个问题&#xff0c;首先打开设置&#xff0c;查找tabsize&#xff0c;进入settings.json。 把"editor.autoIndent"的属性值改为false&#xff0c;即"editor.autoIndent": "false"&#xff0c;就可以了。

vue 过滤器使用的传参说明

在table中&#xff0c;需要对obj的数据类型进行文字转换&#xff0c;例如后台接口返回的姓别值&#xff1a;1&#xff0c;2。其中需要页面根据字典需要把1》男&#xff0c;2》女进行转换。 以前的习惯是每一个过滤方法都写一个方法进行转换&#xff0c;例如&#xff1a; 页面代…

leetcode 970. 强整数(Powerful Integers)

目录 题目描述&#xff1a;示例 1&#xff1a;示例 2&#xff1a;解法&#xff1a;题目描述&#xff1a; 给定两个正整数 x 和 y&#xff0c;如果某一整数等于 x^i y^j&#xff0c;其中整数 i > 0 且 j > 0&#xff0c;那么我们认为该整数是一个强整数。 返回值小于或等…