`
aa87963014
  • 浏览: 154889 次
  • 性别: Icon_minigender_1
  • 来自: 布尼塔尼亚
社区版块
存档分类
最新评论

成倍提升ORM系统SQL性能的一个方法

阅读更多

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()方法就可以了。也没有太大影响,只是编写的时候多了一种选择

 

1
0
分享到:
评论

相关推荐

    ormsql使用示例

    使用ORMSQL插入数据非常直观,只需要创建一个对象并调用`save()`方法: ```java User user = new User(); user.setName("张三"); user.setAge(25); MyApplication.getSqlHelper().get DaoSession().getUserDao()....

    SqlSugar ORM工具箱2.2.7z

    综上所述,"SqlSugar ORM工具箱2.2.7z"提供的这些组件和库,构建了一个强大的ORM解决方案,支持多种数据库系统,包括SQLite、Oracle、PostgreSQL、MySQL和SQL Server,并且集成了Entity Framework的功能。...

    手写orm

    【标题】:“手写ORM”指的是自己动手实现Object-Relational Mapping(对象关系映射)技术,这是一种在软件开发中将数据库操作与业务逻辑解耦的方法。ORM允许开发者使用面向对象的方式来操作数据库,无需关心底层SQL...

    Android-ORM-benchmark,android orm框架的性能比较.zip

    2. **GreenDao**:这是一个高效、轻量级的ORM框架,通过代码生成工具将实体类与数据库表关联,提供了便捷的API进行数据操作。 3. **Realm**: Realm是一款高性能的数据库,它的API设计非常接近于对象存储,支持实时...

    一个灵活而强大的 SQL 字符串构建器库加上一个零配置 ORM .zip

    该软件包的设计初衷是打造一个独立于特定数据库驱动和业务逻辑的SQL构造库,适应企业环境的多种需求,包括使用自定义数据库驱动、遵循专业化的操作标准、集成到异构系统、处理复杂场景下的非标准SQL等。开源后,该...

    嵌入式多态SQL方法实现ORM.pdf

    在详细讨论了ORM实现方法后,文章提出了一个重要的观点:无论采用哪种方法,都需要在前台应用程序中嵌入对象的CRUD操作。CRUD操作包括创建(Create)、检索(Retrieve)、更新(Update)、删除(Delete)四种基本数据库操作...

    rbatis:Rust ORM框架高性能Rust SQL-ORM(基于JSON)

    总结来说,rbatis是Rust开发中的一个强大工具,它利用JSON构建SQL,简化了数据库操作,支持多种数据库,同时借助Rust的异步编程能力实现了高性能。无论你是新手还是经验丰富的开发者,rbatis都能帮助你更高效地管理...

    提升Hibernate性能的魔方——IronTrack SQL.pdf

    Hibernate是一个流行的Java对象关系映射(ORM)框架,它极大地简化了Java应用与数据库的交互。然而,Hibernate的性能和效率问题一直是一个需要特别关注的领域。Hibernate自动生成的SQL语句可能在某些情况下并不是最...

    python酒店管理系统,fastapi框架,采用mysql数据库,采用了tortoise-orm连接数据库

    在本项目中,我们主要探讨的是一个基于Python的酒店管理系统,该系统采用了现代Web开发框架FastAPI以及关系型数据库MySQL,并且利用了Tortoise-ORM进行数据操作。以下是关于这些关键技术点的详细说明。 首先,...

    ORM一键还原系统 v4.1.25.1 官方版.zip

    今天小编要给大家介绍一款功能强大的Windows系统备份和还原工具——ORM一键还原系统。ORM一键还原系统备份速度比ghost还快,压缩率更出色,可以全自动备份系统数据到最大分区,ORM一键还原系统同时支持所有主流...

    ORM客户关系体系统

    在本例中,我们讨论的是一个基于VS2005开发的三层架构ORM客户关系管理系统,它提供了学习源码,非常适合于开发者进行学习和实践。 首先,三层架构是软件设计中的一种常见模式,主要分为表现层(UI)、业务逻辑层...

    hsweb-easy-orm, 简单的orm工具,为动态表单而生.zip

    HSWeb-Easy-ORM 是一个开源的简单对象关系映射(ORM)工具,设计初衷是为了简化动态表单的处理。ORM 工具的核心作用在于桥接数据库与编程语言,允许开发者通过面向对象的方式操作数据库,避免了直接编写 SQL 语句的...

    轻量级、高性能SQL ORM 之 SqlSugar.zip

    6、无需任何配置 ,还你一个干净的model,可以没有主键 ,也可以有多个主键 ,或者多个自增列     代码片段包下载地址  https://github.com/sunkaixuan/SqlSugarSnippets  SqlSugar ORM教程和下载地址 ...

    基于ORM模型的物资管理系统的设计和实现

    本文将深入探讨如何利用ORM模型设计并实现一个高效、便捷的物资管理系统。 ORM,即对象关系映射,是一种编程技术,它将数据库的表结构映射为程序中的对象,使得开发者可以使用面向对象的方式来操作数据库,降低了...

    go 打造的轻量级 orm; 支持多数据库; 性能接近原生(database/sql)

    本篇文章将深入探讨一个基于Go语言的轻量级ORM库——"spellsql",它支持多数据库,并且性能表现接近原生的`database/sql`包。 首先,"spellsql"是一个为Go语言设计的ORM框架,它的主要目标是提供一种简单、高效的...

    Python 手写ORM-我的一个数据库访问工具

    对象-关系映射(Object-Relational Mapping,简称ORM),面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的...

    Android-requery-编译期的Java和Android的ORM和SQL查询库

    `requery` 是一个强大的ORM(对象关系映射)库,专为Java和Android开发者设计,旨在简化数据库操作并提供编译时的SQL查询支持。通过将数据库操作转化为面向对象的方式,它极大地提高了开发效率和代码质量,同时减少...

    ORM使用手册 使用说明. ORM系统采用与微软LINQ to SQL相似的操作方式,支持类似LINQ语法查询与基于轨迹的增删改操作。

    1. 使用代码生成器:可能是一个工具或脚本,根据数据库模式自动生成C#或其他编程语言的代码。 2. 使用生成的代码:生成的代码可以直接导入项目中,开发者可以利用这些预定义的类和方法进行数据库操作。 三、查询...

    SQLServer数据库应用程序性能优化方法

    SQL Server 数据库内核内置了一个基于成本的查询优化器,用于自动优化向系统发出的数据查询操作。查询优化器的处理过程主要包括三个阶段:查询分析、索引选择和连接选择。 1. **查询分析**:这是查询优化的第一阶段...

Global site tag (gtag.js) - Google Analytics