ORM确实事很方便让人不需要大量的写SQL,但是很多人诟病造成SQL性能不好
举个例子,一个功能是修改get用户数据的金币清零然后update
非常简单的一个业务:
User user=dao.getUser(uid); user.setGold(0); dao.updateUser(user);
就这样一个简单的功能,实际会向数据库发送一长串的SQL update语句
如:update user set a=?,b=?,c=?............gold=? where uid = ?
看到这里有人会明白问题出在哪里了,想想一下每个表都有一堆字段, 而实际上我们在各个业务里面只会修改一两个字段,导致每次调用业务都会生产这样一长串的SQL,给数据库来带巨大而且没有必要的压力。
有的人会说:我编程习惯很好,平时都会把调用频率高的方法单独写sql。例如上面修改金币会单独写一个updateGold方法。当然,这是一种很好的习惯并且也能解决这个问题。但是 大部分开发者、大部分业务实现并不会这样做。不是吗?
现在可以通过另外一种方法来实现对原有的ORM系统优化兼顾开发效率和系统性能提升:
/** update之前先比较,只update修改过的列 */ public <T> void compareAndUpdate(T oldT, T newT);
简单的介绍一下就是,通过对比新旧实体,根据修改过的列来生成指定的列到达针对性更新的目的。
//以前: User user=dao.getUser(uid); user.setGold(0); dao.updateUser(user); //产生的sql: update user set a=?,b=?,c=?............gold=? where uid = ? //新的做法: User user=dao.getUser(uid); User oldUser=BeanUtils.cloneBean(user); user.setGold(0); dao.compareAndUpdate(oldUser,user); //产生的sql: update user set gold = ? where uid = ? sql语句的缩短成倍的降低了与数据库通讯的开销,并且大大的降低了数据库压力。 如果上面的写法可能会稍微麻烦点,我们可以再偷点懒: User user=dao.getUser(uid); Object oldBean=BeanUtils.copyBean(user);//这样的区别是copy这段代码即可,不需要经常修改oldBean的类型。因为我们不关心oldBean是什么类型。 user.setGold(0); dao.compareAndUpdate(oldBean,user);
有人会说实体copy和拼接sql也会产生开销,这个是不错。但是这种开销对于数据库的压力来说根本就是九牛一毛不值一提。
compareAndUpdate这个方法是我自己写的ORM框架:freyja-jdbc 里面改写update方法产生的。如果是使用的其他ORM框架 有预留接口的话可以改写下即可。没有接口的话可能需要自己去实现了,方法就是映射实体和字段 生成sql语句
hibernate 好久没碰了,怎么改写hibernate也能达到一样的效果不清楚,有兴趣的可以自己研究下。
最后还是放下相关代码吧
public static Parameter compareAndUpdate(Object oldEntity, Object newEntity) { FreyjaEntity entity = ShardingUtil.getEntity(newEntity.getClass()); List<Object> args = new ArrayList<Object>(); Object idValue = null; BeanMap oldBeanMap = BeanMap.create(oldEntity); BeanMap beanMap = BeanMap.create(newEntity); List<String> columnNameList = new ArrayList<String>(); for (Property p : entity.getProperties().values()) { ShardingProperty s = (ShardingProperty) p; Object newPropertyValue = beanMap.get(p.getName()); if (s.isId()) { idValue = newPropertyValue; continue; } else { Object oldPropertyValue = oldBeanMap.get(p.getName()); if ((newPropertyValue == null && oldBeanMap == null) || newPropertyValue.equals(oldPropertyValue)) {// 值未改变过,不更新 continue; } } columnNameList.add(p.getName()); args.add(newPropertyValue); } args.add(idValue); Parameter parameter = null; if (entity.isSubTable()) { DbResult result = ShardingUtil.engine.getShardingStrategy() .getShardingTableNameById(entity.getTableName(), idValue); parameter = entity.updateByColumn(columnNameList, idValue); parameter.setDbNo(result.getDbNo()); } else { parameter = entity.updateByColumn(columnNameList, null); } parameter.setArgs(args.toArray()); return parameter; } /** 根据指定列生产相关信息 */ public Parameter updateByColumn(List<String> columnNameList, Object idValue) { String set = ""; List<Integer> types = new ArrayList<Integer>(); for (String cn : columnNameList) { ShardingProperty s = (ShardingProperty) getProperties().get(cn); set += s.getDbColumnName() + " = ? ,"; types.add(s.getTypes()); } set = set.substring(0, set.length() - 1); String setSql = " set " + set + " where " + getId().getDbColumnName() + " = ?"; types.add(getId().getTypes()); String sql = null; if (idValue == null) { sql = "update " + getTableName() + setSql; } else {// 分库 DbResult result = ShardingUtil.engine.getShardingStrategy() .getShardingTableNameById(getTableName(), idValue); sql = "update " + result.getTableName() + setSql; } Parameter p = new Parameter(); p.setSql(sql); p.setSqlTypes(ListUtil.toPrimitive(types)); return p; }
----------------------------------------------------------------------------
实际操作了下后发现虽然确实是不错,但是对现有系统和写法有出入。有没有兼容现有系统的写法更智能一些的办法呢。
然后想到了另外一种做法:
public class PersistObj implements Serializable { /** 用于更新compareAndUpdate,并且改属性不会被序列化 */ private transient Object oldBean; get();set(); } //然后让你的实体继承这个对象,这样默认都会有了一个oldBean属性来存放oldBean //再写个通用方法 @Override public <T> T getAndClone(Class<T> clazz, Object id) { T t = super.get(clazz, id); if (t instanceof PersistObj) { PersistObj obj = (PersistObj) t; Object oldBean = BeanUtils.cloneBean(t); obj.setOldBean(oldBean); } return t; } //这样查询的时候就把oldBean存储了。 //之前的update 方法改写下: if (oldT == null) { if (newT instanceof PersistObj) { PersistObj obj = (PersistObj) newT; oldT = (T) obj.getOldBean(); } } 让oldBean从 oldBean属性里面去取。不需要再传递了
这样做后的效果是:
User user = userDao.getAndClone(uid); user.setGold(0); userDao.compareAndUpdate(user); //这里这样写是为了让大家明白如何工作的。 //实际上这些方法是可以替换底层封装的,业务代码不需要修改。 //也就是说可以达到这种写法效果: User user = userDao.getUser(uid); user.setGold(0); userDao.updateUser(user); //是不是和最初一样了,在不影响现有代码的基础上提升性能
这样做后对原有的业务不需要再修改, 因为这些dao方法都是底层的。替换的就可以了。service业务方法不需要变动。
当然,这样写有2个缺点:
1、无法对缓存有效。
2、每次查询都会克隆一遍。
针对第一个问题:当有用到缓存的地方,只能用之前的方法,手动的cloneBean。
针对第二个问题:首先对于不需要这种功能的实体不用getAndClone方法就没有任何影响
对于其他的一般我们查询肯定是为了修改的。大部分逻辑都是这样。所以默认情况影响不大,如果明确不涉及到修改,不使用getAndClone方法就可以了使用其他默认的get()方法就可以了。也没有太大影响,只是编写的时候多了一种选择
相关推荐
超级简单的android 数据库 ormsql使用示例,易懂易学
6、无需任何配置 ,还你一个干净的model,可以没有主键 ,也可以有多个主键 ,或者多个自增列 代码片段包下载地址 https://github.com/sunkaixuan/SqlSugarSnippets SqlSugar ORM教程和下载地址 ...
对象-关系映射(Object-Relational Mapping,简称ORM),面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的...
系统架构+ORM+设计模式 系统架构+ORM+设计模式
本文实例讲述了获取django框架orm query执行的sql语句实现方法。分享给大家供大家参考,具体如下: 利用Django orM 可以很方便的写出很多查询,但有时候,我们需要检查这些利用 orM 生成的 SQL 语句是否正确,就需要...
今天小编要给大家介绍一款功能强大的Windows系统备份和还原工具——ORM一键还原系统。ORM一键还原系统备份速度比ghost还快,压缩率更出色,可以全自动备份系统数据到最大分区,ORM一键还原系统同时支持所有主流...
SqlSugar ORM工具箱2.2.7z
嵌入式多态SQL方法实现ORM.pdf
requery - 编译期的Java和Android的ORM和SQL查询库
目前有很多面向android操作系统的orm库。我们回顾了最流行的,并比较了性能和其他一些参数。
Gosql的 gosql是Golang的简单ORM库。风格: var userList [] UserModelerr := db . FetchAll ( & userList , gosql . Columns ( "id" , "name" ), gosql . Where ( "status" , 1 ), gosql . Where ( "[like]name" , ...
作者requery,源码requery,一个轻量但是强大的编译时ORM与SQL查询生成工具,支持RxJava和Java 8。
3)体积小(不到150kb,仅一个dll) 4)完美支持Sql Server(2000至最新版),MySql,Oracle,Access,Sqlite等数据库 5)支持大量Lambda表达式写法,国产ORM支持度最高,开源中国ORM排行前三 不需要像NHibernate的XML配置...
自己开发了一个小型ORM项目 包含了一个代码生成工具
一款轻量级高性能的ORM,可以不用写SQL语句,分页,事务,多结果集,模型映射,Include,导航模型属性,导航值对象属性,支持多租户,不同租户不同数据库等,支持各种复杂SQL,如:Insert Select From,Update From ...
轻量级,只有一个dll文件(不到200KB),相比于EF,NHibernate这些重量级的ORM框架,实在是太小。 3.支持各种不同主流数据库,例如:SQL Server,MySql,Oracle,Sqlite,Access等等。 4.高性能,接近手写sql 5...
Sqlite ORM 是一个简单的C#类,对Sqlite的操作进行了封装,主要功能包括:表定义、生成,访问,更新等,其中,支持,多表的连接操作,语法类似Linq语法,使用非常方便,附加了使用说明文档。 例如,添加记录操作为...
ORM客户关系体系统 vs2005 三层构架,学习源码 实用
这是很经典的 dao orm 方法 魔乐技术2008的经典
工匠 ADO.NET Micro-ORM到SQL Server。 首先,人们希望保存一个对象图以便一次访问数据库: 客户端上的一条命令, 一个向应用程序服务器的请求, ... 存储库方法应该能够使用一个存储过程读取或保存一个复杂的对象