diff --git a/orm/src/main/java/com/zfoo/orm/accessor/IAccessor.java b/orm/src/main/java/com/zfoo/orm/accessor/IAccessor.java index b74caaf6..2888f76f 100644 --- a/orm/src/main/java/com/zfoo/orm/accessor/IAccessor.java +++ b/orm/src/main/java/com/zfoo/orm/accessor/IAccessor.java @@ -13,14 +13,13 @@ package com.zfoo.orm.accessor; -import com.zfoo.orm.cache.persister.PNode; import com.zfoo.orm.model.IEntity; import org.springframework.lang.Nullable; import java.util.List; /** - * 对数据库进行(增,删,改)的相关方法 + * Methods for writing (insert, delete, update) to the database * * @author godotg */ diff --git a/orm/src/main/java/com/zfoo/orm/accessor/MongodbAccessor.java b/orm/src/main/java/com/zfoo/orm/accessor/MongodbAccessor.java index 0036cd17..6cd108f7 100644 --- a/orm/src/main/java/com/zfoo/orm/accessor/MongodbAccessor.java +++ b/orm/src/main/java/com/zfoo/orm/accessor/MongodbAccessor.java @@ -17,10 +17,8 @@ import com.mongodb.client.model.BulkWriteOptions; import com.mongodb.client.model.Filters; import com.mongodb.client.model.ReplaceOneModel; import com.zfoo.orm.OrmContext; -import com.zfoo.orm.cache.persister.PNode; import com.zfoo.orm.model.IEntity; import com.zfoo.protocol.collection.CollectionUtils; -import com.zfoo.scheduler.util.TimeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,7 +65,7 @@ public class MongodbAccessor implements IAccessor { var result = collection.replaceOne(filter, entity); if (result.getMatchedCount() <= 0) { - // 数据库中没有这个id + // No document with this id found in the database logger.warn("database:[{}] [_id:{}] not exist", entityClazz.getSimpleName(), entity.id()); return false; } @@ -95,7 +93,7 @@ public class MongodbAccessor implements IAccessor { var result = collection.bulkWrite(batchList, new BulkWriteOptions().ordered(false)); if (result.getMatchedCount() != entities.size()) { - // 在数据库的批量更新操作中需要更新的数量和最终更新的数量不相同 + // The expected update count does not match the actual update count in batch operation logger.warn("database:[{}] update size:[{}] not equal with matched size:[{}](some entity of id not exist in database)" , entityClazz.getSimpleName(), entities.size(), result.getMatchedCount()); } diff --git a/orm/src/main/java/com/zfoo/orm/anno/IndexText.java b/orm/src/main/java/com/zfoo/orm/anno/IndexText.java index 1d79f085..e5524646 100644 --- a/orm/src/main/java/com/zfoo/orm/anno/IndexText.java +++ b/orm/src/main/java/com/zfoo/orm/anno/IndexText.java @@ -17,7 +17,7 @@ import org.springframework.aot.hint.annotation.Reflective; import java.lang.annotation.*; /** - * Mongodb不支持中文分词 + * MongoDB does not support CJK text segmentation natively * * @author godotg */ diff --git a/orm/src/main/java/com/zfoo/orm/anno/Version.java b/orm/src/main/java/com/zfoo/orm/anno/Version.java index 9fc59d07..dadae48e 100644 --- a/orm/src/main/java/com/zfoo/orm/anno/Version.java +++ b/orm/src/main/java/com/zfoo/orm/anno/Version.java @@ -17,7 +17,7 @@ import org.springframework.aot.hint.annotation.Reflective; import java.lang.annotation.*; /** - * 一个文档的写入到数据库的version版本,只有使用@EntityCache注解时才有用 + * Document write version; only useful when the entity uses @EntityCache * * @author godotg */ diff --git a/orm/src/main/java/com/zfoo/orm/cache/EntityCache.java b/orm/src/main/java/com/zfoo/orm/cache/EntityCache.java index 468c4b67..3fc1a554 100644 --- a/orm/src/main/java/com/zfoo/orm/cache/EntityCache.java +++ b/orm/src/main/java/com/zfoo/orm/cache/EntityCache.java @@ -60,7 +60,7 @@ public class EntityCache, E extends IEntity> imple public EntityCache(Class entityClass, EntityDef entityDef) { this.clazz = entityClass; this.entityDef = entityDef; - // 创建CacheVersion + // Create CacheVersion var entityWrapper = new EntityWrapper<>(clazz); if (GraalVmUtils.isGraalVM()) { wrapper = entityWrapper; @@ -108,7 +108,7 @@ public class EntityCache, E extends IEntity> imple var entity = OrmContext.getAccessor().load(pk, clazz); - // 如果数据库中不存在则返回null,并将null放入缓存,防止频繁查库 + // Return null if not in DB; cache null to prevent repeated DB queries if (entity == null) { logger.warn("[{}] can not load [pk:{}] and use null to replace it", clazz.getSimpleName(), pk); } @@ -127,7 +127,7 @@ public class EntityCache, E extends IEntity> imple var entity = (E) OrmContext.getAccessor().load(pk, clazz); - // 如果数据库中不存在则给一个默认值 + // If not in DB, use a default value if (entity == null) { entity = wrapper.newEntity(pk); var inserted = OrmContext.getAccessor().insert(entity); @@ -141,7 +141,7 @@ public class EntityCache, E extends IEntity> imple } /** - * 校验需要更新的entity和缓存的entity是否为同一个entity + * Verify that the entity to be updated is the same object as the cached entity */ private PNode fetchCachePnode(E entity, boolean safe) { var id = entity.id(); @@ -151,7 +151,7 @@ public class EntityCache, E extends IEntity> imple caches.put(entity.id(), cachePnode); } - // 比较地址是否相等 + // Compare object references if (entity != cachePnode.getEntity()) { throw new RunException("fetchCachePnode: cache entity [id:{}] not equal with update entity [id:{}]", cachePnode.getEntity().id(), id); } @@ -168,7 +168,7 @@ public class EntityCache, E extends IEntity> imple for (var stack : Thread.currentThread().getStackTrace()) { builder.append(FileUtils.LS).append(stack); } - // 有并发写风险,第一次更新文档的线程和第2次更新更新文档的线程不相等 + // Concurrent write risk: the thread for the 1st and 2nd update are different if (pnodeThread == null) { logger.warn("[{}][id:{}] concurrent write warning, first update [threadId:{}], second update [threadId:{}], current stack trace as following:{}" , entity.getClass().getSimpleName(), entity.id(), pnodeThreadId, currentThreadId, builder); @@ -192,7 +192,7 @@ public class EntityCache, E extends IEntity> imple @Override public void update(E entity) { var cachePnode = fetchCachePnode(entity, true); - // 加128以防止,立刻加载并且立刻修改数据的情况发生时,服务器取到的时间戳相同 + // Add 128ms buffer to prevent same-timestamp edge case when data is loaded and modified immediately cachePnode.setModifiedTime(TimeUtils.now() + 128); } @@ -218,8 +218,8 @@ public class EntityCache, E extends IEntity> imple @Override public void invalidate(PK pk) { - // 游戏业务中,操作最频繁的是update,不是insert,delete,query - // 所以这边并不考虑 + // In game services, update is the most frequent operation, not insert/delete/query + // So other operations are not the main concern here AssertionUtils.notNull(pk); caches.remove(pk); } @@ -238,15 +238,15 @@ public class EntityCache, E extends IEntity> imple } - // 游戏中80%都是执行更新的操作,这样做会极大的提高更新速度 - // 没有并发问题的entity指的是内部没有使用集合或者使用的集合全部支持并发操作 - // 没有并发问题的entity还是在异步线程池Event慢慢更新,有并发问题的entity才放到原来的update线程去更新(第一次update会记录entity所在线程) + // ~80% of game operations are updates; this approach greatly improves update performance + // Thread-safe entity: no collections, or all collections support concurrent access + // Thread-safe entities use async event pool; non-thread-safe entities use the original update thread (tracked on first update) @Override public void persistAll() { if (entityDef.isThreadSafe()) { EventBus.asyncExecute(clazz.hashCode(), () -> persistAllBlock()); } else { - // key为threadId + // key is threadId var updateMap = new HashMap>>(); var initSize = caches.size() >> 2; caches.forEach(new BiConsumer>() { @@ -267,7 +267,7 @@ public class EntityCache, E extends IEntity> imple if (executor == null) { EventBus.asyncExecute(clazz.hashCode(), () -> doPersist(updateList)); } else { - // 使用scheduler均匀的分配入库的时间点,减少数据库的并发写入压力 + // Use scheduler to evenly distribute DB write timestamps to reduce concurrent write pressure SchedulerBus.schedule(() -> executor.execute(() -> doPersist(updateList)), count++ * 128L, TimeUnit.MILLISECONDS); } } @@ -290,7 +290,7 @@ public class EntityCache, E extends IEntity> imple } private void doPersist(List> updateList) { - // 执行更新 + // Execute update if (updateList.isEmpty()) { return; } @@ -318,12 +318,12 @@ public class EntityCache, E extends IEntity> imple var result = collection.bulkWrite(batchList, new BulkWriteOptions().ordered(false)); - //设置修改时间 + // Set modification time long currentTime = TimeUtils.currentTimeMillis(); currentUpdateList.forEach(k->k.resetTime(currentTime)); if (result.getMatchedCount() != entities.size()) { - // 在数据库的批量更新操作中需要更新的数量和最终更新的数量不相同 + // The expected update count does not match the actual update count in batch operation logger.warn("database:[{}] update size:[{}] not equal with matched size:[{}](some entity of id not exist in database)" , clazz.getSimpleName(), entities.size(), result.getMatchedCount()); } @@ -362,7 +362,7 @@ public class EntityCache, E extends IEntity> imple } // mostly because the document that needs to be updated is the same as the document in the database - // 开始执行容错操作(大部分原因都是因为需要更新的文档和数据库的文档相同) + // Begin fallback operation (most cases: cached document already matches database) logger.warn("doPersistWithVersion(): [{}] batch update [{}] not equal to final update [{}], and try to use persistAndCompareVersion() to update every single entity." , clazz.getSimpleName(), currentUpdateList.size(), result.getMatchedCount()); } catch (Throwable t) { @@ -392,22 +392,22 @@ public class EntityCache, E extends IEntity> imple continue; } - // 如果没有版本号,则直接更新数据库 + // No version number: update the database directly var entityVersion = wrapper.gvs(entity); var dbEntityVersion = wrapper.gvs(dbEntity); - // 如果版本号相同,说明已经更新到 + // Same version: already up-to-date if (dbEntityVersion == entityVersion) { continue; } - // 如果数据库版本号较小,说明缓存的数据是最新的,直接写入数据库 + // DB version smaller: cache is newer, write to database if (dbEntityVersion < entityVersion) { OrmContext.getAccessor().update(entity); continue; } - // 数据库版本号较大,说明缓存的数据不是最新的,直接清除缓存,下次重新加载 + // DB version larger: cache is stale, evict it, reload on next access caches.remove(id); load(id); logger.warn("[database:{}] document of entity [id:{}] version [{}] is greater than cache [vs:{}] and reload db entity to cache", clazz.getSimpleName(), id, dbEntityVersion, entityVersion); diff --git a/orm/src/main/java/com/zfoo/orm/cache/IEntityCache.java b/orm/src/main/java/com/zfoo/orm/cache/IEntityCache.java index 3f75aacb..353831b5 100644 --- a/orm/src/main/java/com/zfoo/orm/cache/IEntityCache.java +++ b/orm/src/main/java/com/zfoo/orm/cache/IEntityCache.java @@ -15,7 +15,6 @@ package com.zfoo.orm.cache; import com.zfoo.orm.model.IEntity; import org.springframework.lang.Nullable; -import java.util.List; import java.util.function.BiConsumer; /** @@ -25,7 +24,7 @@ public interface IEntityCache, E extends IEntity> /** * EN: Load data from the database to the cache and return null if the database does not exist. - * CN: 从数据库中加载数据到缓存,如果数据库不存在则返回null。 + * Load entity from database into cache; returns null if not found. */ @Nullable E load(PK pk); @@ -34,13 +33,13 @@ public interface IEntityCache, E extends IEntity> * EN: Load data from the database into the cache, and if the database does not exist, * return a default example with the id as the passed in value and put it into storage. *

- * CN: 从数据库中加载数据到缓存,如果数据库不存在则返回一个id为传入值的默认示例,并且入库。(设置了索引唯一的无法使用该方法) + * Load entity from DB into cache; if not found, creates a default instance with the given id and inserts it. (Cannot be used with unique-indexed entities) */ E loadOrCreate(PK pk); /** * EN: Get the data in the cache or returns null if it does not exist in the cache. - * CN: 获取缓存中的数据,如果缓存中不存在则返回null。 + * Get the cached entity; returns null if not cached. */ @Nullable E get(PK pk); @@ -49,36 +48,36 @@ public interface IEntityCache, E extends IEntity> * EN: Update the timestamp of the cache in the memory. * The first update() will record the thread number, and a thread-safe warning will be given if the thread number of next update() is not equal with the first time *

- * CN: 更新缓存中的数据,只更新缓存的时间戳,并通过一定策略写入到数据库 - * 第一次update()会记录更新的线程号,后面如果发现update()所在的线程号和第一次不一致会给出线程安全的警告 + * Update cached entity; only refreshes the timestamp and writes to DB according to the persistence strategy. + * The first update() call records the thread ID; subsequent calls from a different thread will trigger a thread-safety warning. */ void update(E entity); /** * EN: The similar as update() will not check whether the updated thread number is equal. *

- * CN: 同update(),不会校验更新的线程是否一致 + * Same as update(), but does not check for thread consistency. */ void updateUnsafe(E entity); /** * EN: Update the cached entity and write it to the database immediately *

- * CN: 更新缓存中的数据,立刻写入到数据库 + * Update cached entity and immediately persist to database. */ void updateNow(E entity); /** * EN: The similar as updateNow() will not check whether the updated thread number is equal. *

- * CN: 同updateNow(),不会校验更新的线程是否一致 + * Same as updateNow(), but does not check for thread consistency. */ void updateUnsafeNow(E entity); /** * EN: The data in the database will not be deleted, only the cached entity will be deleted. *

- * CN: 不会删除数据库中的数据,只会删除缓存数据 + * Remove from cache only; does not delete from database. * * @param pk primary key */ @@ -87,7 +86,7 @@ public interface IEntityCache, E extends IEntity> /** * EN: Persistence cached entity in memory *

- * CN: 持久化缓存数据 + * Persist all cached data to database. * * @param pk primary key */ diff --git a/orm/src/main/java/com/zfoo/orm/cache/persister/CronOrmPersister.java b/orm/src/main/java/com/zfoo/orm/cache/persister/CronOrmPersister.java index efa4dfb5..2f2ea84f 100644 --- a/orm/src/main/java/com/zfoo/orm/cache/persister/CronOrmPersister.java +++ b/orm/src/main/java/com/zfoo/orm/cache/persister/CronOrmPersister.java @@ -13,7 +13,6 @@ package com.zfoo.orm.cache.persister; -import com.zfoo.event.manager.EventBus; import com.zfoo.orm.OrmContext; import com.zfoo.orm.cache.EntityCache; import com.zfoo.orm.model.EntityDef; @@ -34,12 +33,12 @@ public class CronOrmPersister extends AbstractOrmPersister { private static final Logger logger = LoggerFactory.getLogger(CronOrmPersister.class); /** - * 持久化默认的延迟时间 + * Default delay time before persisting */ private static final long DEFAULT_DELAY = 30 * TimeUtils.MILLIS_PER_SECOND; /** - * cron表达式 + * Cron expression */ private final CronExpression cronExpression; @@ -64,7 +63,7 @@ public class CronOrmPersister extends AbstractOrmPersister { if (delay < 0) { delay = DEFAULT_DELAY; - logger.error("计算[cron:{}]表达式发生错误,当前时间[now:{}],计算出来的时间[nextTimestamp:{}],两者之差小于0" + logger.error("Error computing [cron:{}] expression, now[{}], nextTimestamp[{}], difference < 0" , cronExpression.toString(), now, nextTimestamp); } } catch (Exception e) { diff --git a/orm/src/main/java/com/zfoo/orm/cache/persister/PNode.java b/orm/src/main/java/com/zfoo/orm/cache/persister/PNode.java index 089f0c22..f7bc5495 100644 --- a/orm/src/main/java/com/zfoo/orm/cache/persister/PNode.java +++ b/orm/src/main/java/com/zfoo/orm/cache/persister/PNode.java @@ -19,20 +19,20 @@ import com.zfoo.scheduler.util.TimeUtils; /** * Persister Node *

- * 需要持久化的一个节点 + * A node that needs to be persisted * * @author godotg */ public class PNode, E extends IEntity> { - // 写入数据库的时间 + // Time when written to database private volatile long writeToDbTime; - // 修改数据的时间 + // Time when data was modified private volatile long modifiedTime; private volatile E entity; - // 记录最初访问时的线程信息 + // Record the thread info at first access private long threadId; public PNode(E entity) { diff --git a/orm/src/main/java/com/zfoo/orm/cache/persister/TimeOrmPersister.java b/orm/src/main/java/com/zfoo/orm/cache/persister/TimeOrmPersister.java index 7cbc7dee..dee3a356 100644 --- a/orm/src/main/java/com/zfoo/orm/cache/persister/TimeOrmPersister.java +++ b/orm/src/main/java/com/zfoo/orm/cache/persister/TimeOrmPersister.java @@ -13,7 +13,6 @@ package com.zfoo.orm.cache.persister; -import com.zfoo.event.manager.EventBus; import com.zfoo.orm.OrmContext; import com.zfoo.orm.cache.EntityCache; import com.zfoo.orm.model.EntityDef; @@ -28,7 +27,7 @@ import java.util.concurrent.TimeUnit; public class TimeOrmPersister extends AbstractOrmPersister { /** - * 执行的频率 + * Execution frequency (in ms) */ private final long rate; @@ -36,7 +35,7 @@ public class TimeOrmPersister extends AbstractOrmPersister { super(entityDef, entityCaches); this.rate = Long.parseLong(entityDef.getPersisterStrategy().getConfig()); if (this.rate <= 0) { - throw new RuntimeException(StringUtils.format("刷新频率[{}]不能小于0", rate)); + throw new RuntimeException(StringUtils.format("refresh rate [{}] must be >= 0", rate)); } } diff --git a/orm/src/main/java/com/zfoo/orm/cache/wrapper/EnhanceUtils.java b/orm/src/main/java/com/zfoo/orm/cache/wrapper/EnhanceUtils.java index c8c51d77..4a284813 100644 --- a/orm/src/main/java/com/zfoo/orm/cache/wrapper/EnhanceUtils.java +++ b/orm/src/main/java/com/zfoo/orm/cache/wrapper/EnhanceUtils.java @@ -32,7 +32,7 @@ import java.lang.reflect.Modifier; public abstract class EnhanceUtils { static { - // 适配Tomcat,因为Tomcat不是用的默认的类加载器,而Javassist用的是默认的加载器 + // Adapt for Tomcat: Tomcat uses its own class loader, but Javassist uses the default one var classArray = new Class[]{ IEntityWrapper.class }; @@ -84,39 +84,39 @@ public abstract class EnhanceUtils { Method getVersionMethod = entityWrapper.getGetVersionMethod(); Method setVersionMethod = entityWrapper.getSetVersionMethod(); - // 定义类名称 + // Define class name CtClass enhanceClazz = classPool.makeClass(EnhanceUtils.class.getName() + StringUtils.capitalize(NamespaceHandler.ORM) + UuidUtils.getLocalIntId()); enhanceClazz.addInterface(classPool.get(IEntityWrapper.class.getName())); - // 定义类实现的接口方法newEntity + // Define interface method: newEntity CtMethod newEntityMethod = new CtMethod(classPool.get(IEntity.class.getName()), "newEntity", classPool.get(new String[]{Comparable.class.getName()}), enhanceClazz); newEntityMethod.setModifiers(Modifier.PUBLIC + Modifier.FINAL); String newEntityMethodBody = StringUtils.format("{ {} entity = new {}(); entity.{}({}); return entity; }", clazz.getName(), clazz.getName(), setIdMethod.getName(), toOriginType(idField)); newEntityMethod.setBody(newEntityMethodBody); enhanceClazz.addMethod(newEntityMethod); - // 定义类实现的接口方法versionFieldName + // Define interface method: versionFieldName CtMethod versionFieldNameMethod = new CtMethod(classPool.get(String.class.getName()), "versionFieldName", null, enhanceClazz); versionFieldNameMethod.setModifiers(Modifier.PUBLIC + Modifier.FINAL); String versionFieldNameMethodBody = versionField == null ? "{ return null; }" : StringUtils.format("{ return \"{}\"; }", entityWrapper.versionFieldName()); versionFieldNameMethod.setBody(versionFieldNameMethodBody); enhanceClazz.addMethod(versionFieldNameMethod); - // 定义类实现的接口方法gvs + // Define interface method: gvs CtMethod gvsMethod = new CtMethod(classPool.get(long.class.getName()), "gvs", classPool.get(new String[]{IEntity.class.getName()}), enhanceClazz); gvsMethod.setModifiers(Modifier.PUBLIC + Modifier.FINAL); String gvsMethodBody = getVersionMethod == null ? "{ return 0L; }" : StringUtils.format("{ return (({})$1).{}(); }", clazz.getName(), getVersionMethod.getName()); gvsMethod.setBody(gvsMethodBody); enhanceClazz.addMethod(gvsMethod); - // 定义类实现的接口方法svs + // Define interface method: svs CtMethod svsMethod = new CtMethod(classPool.get(void.class.getName()), "svs", classPool.get(new String[]{IEntity.class.getName(), long.class.getName()}), enhanceClazz); svsMethod.setModifiers(Modifier.PUBLIC + Modifier.FINAL); String svsMethodBody = setVersionMethod == null ? "{}" : StringUtils.format("{ (({})$1).{}($2); }", clazz.getName(), setVersionMethod.getName()); svsMethod.setBody(svsMethodBody); enhanceClazz.addMethod(svsMethod); - // 释放缓存 + // Release cache enhanceClazz.detach(); Class resultClazz = enhanceClazz.toClass(IEntityWrapper.class); diff --git a/orm/src/main/java/com/zfoo/orm/cache/wrapper/IEntityWrapper.java b/orm/src/main/java/com/zfoo/orm/cache/wrapper/IEntityWrapper.java index 2cbf1203..ea70ac98 100644 --- a/orm/src/main/java/com/zfoo/orm/cache/wrapper/IEntityWrapper.java +++ b/orm/src/main/java/com/zfoo/orm/cache/wrapper/IEntityWrapper.java @@ -23,23 +23,23 @@ public interface IEntityWrapper, E extends IEntity E newEntity(PK id); /** - * version field的名称 + * Name of the version field */ String versionFieldName(); /** - * 一个文档的写入到数据库的version版本,version的get和set方法 + * Document write version; getter and setter for the version field. *

- * 写入一条数据到数据库的时候会对比当前entity的vs和数据库的vs是不是一样,不是一样的话无法写入,一致的话就写入数据并且让vs自增+1. - * 主要是为了防止多个服务器去操作同一条数据,保证在分布式环境的数据一致性;简单的说就是一个数据版本号的简单实现,版本号一致才能写入数据。 - * 在分布式环境中,会存在有状态服务器,比如网关路由一个用户或玩家的数据到a服务器,这个时候你加了一个b服务器,有可能下一条数据就被路由到b。 - * 虽然用了一致性hash的负载均衡算法,但是一样有概率会让某些消息路由到不同服务器。 - * 这个时候版本号就可以保证只有一台服务器可以对数据库做操作,不用担心多个服务器去操作数据。 - * 这是一个容错的操作,真实环境下很少发生。为了高性能必须要把服务器做成有状态的,这个容错操作就是最后一道保证数据一致的方案。 - * mongodb更新的时候是原子的,并发更新同一条数据只有一条数据会写入,第二条数据写入的时候版本号已经不一致了,所以老版本号的数据无法写入。 - * 写入的时候只要发现版本号不一致,就让缓存失效,重新读取数据库最新的数据。 + * On write, compares the entity's version with the DB version; if they differ the write is rejected; if equal, data is written and version is incremented. + * Prevents multiple servers from modifying the same document simultaneously, ensuring data consistency in a distributed environment. + * In a distributed environment, stateful servers exist; a gateway may route a user to server A, but after adding server B, the next request might go to B. + * Even with consistent hashing, there is still a chance that some requests are routed to different servers. + * The version number ensures only one server can write to the database at a time. + * This is a fallback operation; it rarely occurs in production. It is the last line of defense for data consistency in high-performance stateful servers. + * MongoDB updates are atomic; under concurrency only one write wins. The second writer's version will be stale and rejected. + * When a version mismatch is detected on write, invalidate the cache and reload from DB. *

- * 分布式环境下,要想服务器性能好,就要做成有状态的,有状态就要遇到这种多服对同一条数据写入问题,这是一个千古难以解决的问题,进退两难。 + * For high performance in a distributed system you need stateful servers, but stateful servers face this multi-writer consistency dilemma — an ancient unsolved trade-off. */ long gvs(E entity); diff --git a/orm/src/main/java/com/zfoo/orm/codec/MapCodec.java b/orm/src/main/java/com/zfoo/orm/codec/MapCodec.java index 58ee60b6..086d0f9d 100644 --- a/orm/src/main/java/com/zfoo/orm/codec/MapCodec.java +++ b/orm/src/main/java/com/zfoo/orm/codec/MapCodec.java @@ -25,7 +25,7 @@ import java.util.Map; import java.util.function.Function; /** - * 基础类型作为key的map解析器 (key 默认不能为null) + * Map codec supporting primitive types as keys (key cannot be null by default) * * @author lqh */ diff --git a/orm/src/main/java/com/zfoo/orm/codec/MapCodecProvider.java b/orm/src/main/java/com/zfoo/orm/codec/MapCodecProvider.java index ea040d65..a2a54a2d 100644 --- a/orm/src/main/java/com/zfoo/orm/codec/MapCodecProvider.java +++ b/orm/src/main/java/com/zfoo/orm/codec/MapCodecProvider.java @@ -20,8 +20,8 @@ import org.bson.codecs.pojo.TypeWithTypeParameters; import java.util.Map; /** - * MongoDB官方驱动Map的key只能为String,为了支持key为其它基础类型的Map有了MapCodecProvider - * java的8种基础类型目前全部支持(char,boolean,byte,short,int,long,float,double) + * The official MongoDB driver only supports String keys; MapCodecProvider adds support for primitive-type keys + * All 8 Java primitive types are supported: char, boolean, byte, short, int, long, float, double * * @author lqh */ diff --git a/orm/src/main/java/com/zfoo/orm/codec/MapKeyCodecEnum.java b/orm/src/main/java/com/zfoo/orm/codec/MapKeyCodecEnum.java index 7cee098d..98c00eb2 100644 --- a/orm/src/main/java/com/zfoo/orm/codec/MapKeyCodecEnum.java +++ b/orm/src/main/java/com/zfoo/orm/codec/MapKeyCodecEnum.java @@ -17,7 +17,7 @@ import java.util.function.Function; import java.util.stream.Collectors; /** - * 基础类型枚举 + * Enum for supported primitive types * * @author lqh */ diff --git a/orm/src/main/java/com/zfoo/orm/config/PersisterTypeEnum.java b/orm/src/main/java/com/zfoo/orm/config/PersisterTypeEnum.java index 89e5d98c..60af3146 100644 --- a/orm/src/main/java/com/zfoo/orm/config/PersisterTypeEnum.java +++ b/orm/src/main/java/com/zfoo/orm/config/PersisterTypeEnum.java @@ -50,7 +50,7 @@ public enum PersisterTypeEnum { return persister; } } - throw new IllegalArgumentException(StringUtils.format("无效的持久化类型[persisterType:{}]", persisterType)); + throw new IllegalArgumentException(StringUtils.format("invalid persister type [persisterType:{}]", persisterType)); } public abstract IOrmPersister createPersister(EntityDef entityDef, EntityCache entityCaches); diff --git a/orm/src/main/java/com/zfoo/orm/convention/AnnotationConvention.java b/orm/src/main/java/com/zfoo/orm/convention/AnnotationConvention.java index 1b4f6587..62b6bd26 100644 --- a/orm/src/main/java/com/zfoo/orm/convention/AnnotationConvention.java +++ b/orm/src/main/java/com/zfoo/orm/convention/AnnotationConvention.java @@ -11,7 +11,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; /** - * 注解约定 + * Annotation naming conventions * * @author Sando */ diff --git a/orm/src/main/java/com/zfoo/orm/manager/IOrmManager.java b/orm/src/main/java/com/zfoo/orm/manager/IOrmManager.java index b2bd7092..af934dc3 100644 --- a/orm/src/main/java/com/zfoo/orm/manager/IOrmManager.java +++ b/orm/src/main/java/com/zfoo/orm/manager/IOrmManager.java @@ -34,7 +34,7 @@ public interface IOrmManager { /** * EN:Get Mongo Client, through which you can get other databases or do some other complex operations - * CN:获取MongoClient,通过MongoClient可以获取到其它数据库或者做一些其它的复杂操作 + * Get the MongoClient instance for accessing other databases or performing complex operations */ MongoClient mongoClient(); @@ -43,12 +43,12 @@ public interface IOrmManager { Collection> getAllEntityCaches(); /** - * 基于对象的orm操作 + * Object-oriented ORM operations */ , E extends IEntity> MongoCollection getCollection(Class entityClazz); /** - * 更加细粒度的操作 + * Fine-grained operations */ MongoCollection getCollection(String collection); diff --git a/orm/src/main/java/com/zfoo/orm/manager/OrmManager.java b/orm/src/main/java/com/zfoo/orm/manager/OrmManager.java index 9dbef012..c2592b39 100644 --- a/orm/src/main/java/com/zfoo/orm/manager/OrmManager.java +++ b/orm/src/main/java/com/zfoo/orm/manager/OrmManager.java @@ -75,7 +75,7 @@ public class OrmManager implements IOrmManager { private MongoDatabase mongodbDatabase; /** - * 全部的Entity定义,key为对应的class,value为当前的Entity是否在当前项目中以缓存的形式使用 + * All Entity definitions; key=class, value=whether the Entity is used with caching in the current project */ private final Map, Boolean> allEntityCachesUsableMap = new HashMap<>(); @@ -117,7 +117,7 @@ public class OrmManager implements IOrmManager { .builder() .codecRegistry(codecRegistry); - // 设置数据库地址 + // Set database host/address var hostConfig = ormConfig.getHost(); if (CollectionUtils.isNotEmpty(hostConfig.getAddress())) { var hostList = hostConfig.getAddress().values() @@ -132,7 +132,7 @@ public class OrmManager implements IOrmManager { mongoBuilder.applyToClusterSettings(builder -> builder.hosts(hostList)); } - // 设置数据库账号密码 + // Set database credentials if (StringUtils.isNotBlank(hostConfig.getUser()) && StringUtils.isNotBlank(hostConfig.getPassword())) { var authSource = StringUtils.isEmpty(hostConfig.getAuthSource()) ? "admin" @@ -140,14 +140,14 @@ public class OrmManager implements IOrmManager { mongoBuilder.credential(MongoCredential.createCredential(hostConfig.getUser(), authSource, hostConfig.getPassword().toCharArray())); } - // 设置连接池的大小 + // Set connection pool size var maxConnection = Runtime.getRuntime().availableProcessors() * 2 + 1; mongoBuilder.applyToConnectionPoolSettings(builder -> builder.maxSize(maxConnection).minSize(1)); mongoClient = MongoClients.create(mongoBuilder.build()); mongodbDatabase = mongoClient.getDatabase(hostConfig.getDatabase()); - // 创建索引 + // Create indexes for (var entry : entityDefMap.entrySet()) { var entityClass = entry.getKey(); var entityDef = entry.getValue(); @@ -207,7 +207,7 @@ public class OrmManager implements IOrmManager { var applicationContext = OrmContext.getApplicationContext(); var componentBeans = applicationContext.getBeansWithAnnotation(Component.class); for (var bean : componentBeans.values()) { - //防止被CGLIB代理时 直接赋值无效 + // Prevent direct assignment from being ignored when the class is CGLIB-proxied var targetBean = Objects.requireNonNullElse(AopProxyUtils.getSingletonTarget(bean), bean); ReflectionUtils.filterFieldsInClass(targetBean.getClass() , field -> field.isAnnotationPresent(EntityCacheAutowired.class) @@ -215,7 +215,7 @@ public class OrmManager implements IOrmManager { Type type = field.getGenericType(); if (!(type instanceof ParameterizedType)) { - // 注入的变量类型需要是泛型类 + // Injected field must be a generic type throw new RuntimeException(StringUtils.format("The variable [{}] is not of type generic", field.getName())); } @@ -225,7 +225,7 @@ public class OrmManager implements IOrmManager { IEntityCache entityCaches = entityCachesMap.get(entityClazz); if (entityCaches == null) { - // entity-package需要配置到可以扫描到EntityCache注解的包 + // entity-package must be configured to scan packages where @EntityCache annotations are used throw new RunException("The EntityCache object does not exist, please check that [entity-package:{}] and [entityCaches:{}] are configured in the correct position", ormConfig.getEntityPackage(), entityClazz); } @@ -256,7 +256,7 @@ public class OrmManager implements IOrmManager { throw new RunException("EntityCaches that do not have [] defined", clazz.getCanonicalName()); } if (!usable) { - // Orm没有使用EntityCacheAutowired,为了节省内存提前释放了它;只有使用EntityCacheAutowired注解的Entity才能被动态获取 + // Orm does not use EntityCacheAutowired here to save memory; only entities annotated with @EntityCacheAutowired can be retrieved dynamically throw new RunException("Orm does not use [] EntityCacheAutowired annotation, which are released in advance to save memory", clazz.getCanonicalName()); } @SuppressWarnings("unchecked") @@ -367,9 +367,9 @@ public class OrmManager implements IOrmManager { public EntityDef parserEntityDef(Class> clazz) { var hasUnsafeCollection = hasUnsafeCollection(clazz); - // 校验id字段的格式 + // Validate the id field format checkIdField(clazz); - // 校验version字段的格式 + // Validate the version field format checkVersionField(clazz); var cacheStrategies = ormConfig.getCaches(); @@ -377,18 +377,18 @@ public class OrmManager implements IOrmManager { var cacheStrategy = CacheStrategy.DEFAULT; var persisterStrategy = PersisterStrategy.DEFAULT; - // Entity如果有被@EntityCache注解标识,则使用被标识的缓存和持久化策略,否则使用默认的策略 + // If the Entity is annotated with @EntityCache, use the specified cache/persist strategy; otherwise use the default if (clazz.isAnnotationPresent(com.zfoo.orm.anno.EntityCache.class)) { var entityCache = clazz.getAnnotation(com.zfoo.orm.anno.EntityCache.class); var cache = entityCache.cache().value(); var cacheStrategyOptional = cacheStrategies.stream().filter(it -> it.getStrategy().equals(cache)).findFirst(); - // Entity需要有@Cache注解的缓存策略 + // Entity must have a @Cache annotation for cache strategy AssertionUtils.isTrue(cacheStrategyOptional.isPresent(), "No Entity[{}] @Cache policy found[{}]", clazz.getSimpleName(), cache); cacheStrategy = cacheStrategyOptional.get(); var persister = entityCache.persister().value(); var persisterStrategyOptional = persisterStrategies.stream().filter(it -> it.getStrategy().equals(persister)).findFirst(); - // 实体类Entity需要有持久化策略 + // Entity must have a persistence strategy defined AssertionUtils.isTrue(persisterStrategyOptional.isPresent(), "Entity[{}] No persistence strategy found[{}]", clazz.getSimpleName(), persister); persisterStrategy = persisterStrategyOptional.get(); } else { @@ -425,26 +425,26 @@ public class OrmManager implements IOrmManager { } private void checkIdField(Class clazz) { - // 是否实现了IEntity接口 + // Check if the class implements IEntity AssertionUtils.isTrue(IEntity.class.isAssignableFrom(clazz), "The entity:[{}] annotated by the [{}] annotation does not implement the interface [{}]" , com.zfoo.orm.anno.EntityCache.class.getName(), clazz.getCanonicalName(), IEntity.class.getCanonicalName()); - // 校验id字段和id()方法的格式,一个Entity类只能有一个@Id注解 + // Validate the id field and id() method; an Entity class may only have one @Id annotation var idFields = ReflectionUtils.getFieldsByAnnoInPOJOClass(clazz, Id.class); AssertionUtils.isTrue(ArrayUtils.isNotEmpty(idFields) && idFields.length == 1 , "The Entity[{}] must have only one @Id annotation (if it is indeed marked with an Id annotation, be careful not to use the Stored Id annotation)", clazz.getSimpleName()); var idField = idFields[0]; var idFieldType = idField.getType(); - // idField必须用private修饰 + // idField must be declared private AssertionUtils.isTrue(Modifier.isPrivate(idField.getModifiers()), "The id of the Entity[{}] must be private", clazz.getSimpleName()); - // id的get方法的返回类型要和id字段一样,setId在hasUnsafeCollection方法中已经校验过了 + // The return type of the id getter must match the id field type; setId is already validated in hasUnsafeCollection var getIdMethod = ReflectionUtils.getMethodByNameInPOJOClass(clazz, FieldUtils.fieldToGetMethod(clazz, idField)); var returnTypeOfGetIdMethod = getIdMethod.getReturnType(); AssertionUtils.isTrue(returnTypeOfGetIdMethod.equals(idFieldType), "[{}] getIdMethod:[{}] return type:[{}] must be equal with type id:[{}]" , clazz.getSimpleName(), getIdMethod.getName(),returnTypeOfGetIdMethod.getName(), idFieldType.getName()); - // 随机给id字段赋值,然后调用id()方法,看看两者的返回值是不是一样的,避免出错 + // Assign a random value to the id field and call id(), verify they match to avoid bugs var entityInstance = ReflectionUtils.newInstance(clazz); Object idFiledValue = null; if (idFieldType.equals(int.class) || idFieldType.equals(Integer.class)) { @@ -468,23 +468,23 @@ public class OrmManager implements IOrmManager { var idMethodOptional = Arrays.stream(ReflectionUtils.getAllMethods(clazz)).filter(it -> it.getName().equalsIgnoreCase("id")) .filter(it -> it.getParameterCount() <= 0) .findFirst(); - // 实体类Entity必须重写id()方法 + // Entity class must override the id() method AssertionUtils.isTrue(idMethodOptional.isPresent(), "The Entity[{}] must override the id() method", clazz.getSimpleName()); var idMethod = idMethodOptional.get(); ReflectionUtils.makeAccessible(idMethod); var idMethodReturnValue = ReflectionUtils.invokeMethod(entityInstance, idMethod); - // 实体类Entity的id字段的返回值field和id方法的返回值method必须相等 + // The id field value and id() method return value must be equal AssertionUtils.isTrue(idFiledValue.equals(idMethodReturnValue), "The return value id [field:{}] of the Entity[{}] and the return value id [method:{}] are not equal, please check whether the id() method is implemented correctly" , clazz.getSimpleName(), idFiledValue, idMethodReturnValue); } public void checkVersionField(Class clazz) { - // @Version标识的字段必须是long类型 + // @Version annotated field must be of type long var versionFields = ReflectionUtils.getFieldsByAnnoInPOJOClass(clazz, Version.class); if (ArrayUtils.isNotEmpty(versionFields)) { AssertionUtils.isTrue(versionFields.length == 1, "The Entity[{}] must have only one @Version annotation", clazz.getSimpleName()); var versionField = versionFields[0]; - // idField必须用private修饰 + // idField must be declared private AssertionUtils.isTrue(Modifier.isPrivate(versionField.getModifiers()), "The version of the Entity[{}] must be private", clazz.getSimpleName()); AssertionUtils.isTrue(versionField.getType().equals(long.class), "The version type of the Entity[{}] must be long", clazz.getSimpleName()); } @@ -495,19 +495,19 @@ public class OrmManager implements IOrmManager { Map.class, HashMap.class, LinkedHashMap.class, TreeMap.class); private boolean hasUnsafeCollection(Class clazz) { - // 是否为一个简单的javabean,为了防止不同层对象混用造成潜在的并发问题,特别是网络层和po层混用 + // Verify the class is a simple JavaBean to prevent concurrency issues from mixing layers (e.g. network layer and persistence layer) ReflectionUtils.assertIsPojoClass(clazz); - // 不能是泛型类 + // Must not be a generic class AssertionUtils.isTrue(ArrayUtils.isEmpty(clazz.getTypeParameters()), "[class:{}] can't be a generic class", clazz.getCanonicalName()); - // 必须要有一个空的构造器 + // Must have a no-arg constructor if (!clazz.isRecord()) { ReflectionUtils.publicEmptyConstructor(clazz); } - // 不能使用Storage的Index注解 + // Must not use the Storage module's @Index annotation var storageIndexes = ReflectionUtils.getFieldsByAnnoNameInPOJOClass(clazz, "com.zfoo.storage.anno.Index"); if (ArrayUtils.isNotEmpty(storageIndexes)) { - // 在Orm中只能使用Orm的Index注解,不能使用Storage的Index注解,为了避免不必要的误解和增强项目的健壮性,禁止这样使用 + // Only Orm's own @Index annotation is allowed in Orm; using Storage's @Index is forbidden to avoid confusion and ensure robustness throw new RunException("only the Index annotation of Orm can be used, not the Index annotation of Storage, and it is forbidden to use it in order to avoid unnecessary misunderstandings and enhance the robustness of the project"); } @@ -517,46 +517,46 @@ public class OrmManager implements IOrmManager { for (var field : filedList) { - // entity必须包含属性的get和set方法 + // Entity must have getters and setters for all fields FieldUtils.fieldToGetMethod(clazz, field); if (!clazz.isRecord()) { FieldUtils.fieldToSetMethod(clazz, field); } - // 是一个基本类型变量 + // Is a primitive type variable var fieldType = field.getType(); if (isBaseType(fieldType)) { // do nothing } else if (fieldType.isArray()) { - // 是一个数组 + // Is an array Class arrayClazz = fieldType.getComponentType(); - // ORM的数组类型只支持byte[] + // ORM only supports byte[] arrays AssertionUtils.isTrue(arrayClazz == byte.class, "The array type of ORM[class:{}] only supports byte[]", clazz.getCanonicalName()); } else if (Set.class.isAssignableFrom(fieldType)) { - // 是一个Set + // Is a Set hasUnsafeCollection |= unsafeCollections.contains(fieldType); var type = field.getGenericType(); - // field必须泛型类 + // Field must declare a generic type AssertionUtils.isTrue(type instanceof ParameterizedType, "[class:{}] type declaration is incorrect, not a generic class[field:{}]", clazz.getCanonicalName(), field.getName()); var types = ((ParameterizedType) type).getActualTypeArguments(); - // 必须声明Set的泛型类 + // Must declare the generic type for Set AssertionUtils.isTrue(types.length == 1, "Set type declaration in [class:{}] is incorrect, and the generic class must be declared in [field:{}]", clazz.getCanonicalName(), field.getName()); hasUnsafeCollectionInner(clazz, types[0]); } else if (List.class.isAssignableFrom(fieldType)) { - // 是一个List + // Is a List hasUnsafeCollection |= unsafeCollections.contains(fieldType); var type = field.getGenericType(); - // field必须泛型类 + // Field must declare a generic type AssertionUtils.isTrue(type instanceof ParameterizedType, "[class:{}] type declaration is incorrect, not a generic class[field:{}]", clazz.getCanonicalName(), field.getName()); - // 必须声明List的泛型类 + // Must declare the generic type for List var types = ((ParameterizedType) type).getActualTypeArguments(); AssertionUtils.isTrue(types.length == 1, "List type declaration in [class:{}] is incorrect, and the generic class must be declared in [field:{}]", clazz.getCanonicalName(), field.getName()); hasUnsafeCollectionInner(clazz, types[0]); } else if (Map.class.isAssignableFrom(fieldType)) { - // 是Map接口类型 + // Is a Map type hasUnsafeCollection |= unsafeCollections.contains(fieldType); var type = field.getGenericType(); @@ -591,7 +591,7 @@ public class OrmManager implements IOrmManager { private boolean hasUnsafeCollectionInner(Class currentEntityClass, Type type) { if (type instanceof ParameterizedType) { - // 泛型类 + // Generic type Class clazz = (Class) ((ParameterizedType) type).getRawType(); if (Set.class.isAssignableFrom(clazz)) { // Set> @@ -615,11 +615,11 @@ public class OrmManager implements IOrmManager { // do nothing return false; } else if (clazz.getComponentType() != null) { - // ORM不支持多维数组或集合嵌套数组类型,仅支持一维数组 + // ORM does not support multi-dimensional arrays or collections nested with arrays; only 1D arrays are supported throw new RunException("class:[{}] type:[{}] does not support multi-dimensional arrays or nested arrays, and only supports one-dimensional arrays" , currentEntityClass.getSimpleName(), clazz.getSimpleName()); } else if (clazz.equals(List.class) || clazz.equals(Set.class) || clazz.equals(Map.class)) { - // ORM不支持集合嵌套数组类型 + // ORM does not support collections containing array types throw new RunException("orm do not support the combination of arrays and collections with the class:[{}] type:[{}]", currentEntityClass.getSimpleName(), clazz.getSimpleName()); } else if (clazz.equals(currentEntityClass)) { return false; diff --git a/orm/src/main/java/com/zfoo/orm/model/EntityDef.java b/orm/src/main/java/com/zfoo/orm/model/EntityDef.java index 1ae1a9af..b5ea2664 100644 --- a/orm/src/main/java/com/zfoo/orm/model/EntityDef.java +++ b/orm/src/main/java/com/zfoo/orm/model/EntityDef.java @@ -21,7 +21,7 @@ import java.util.Map; */ public class EntityDef { - // 线程安全指的是内部没有使用集合或者使用的集合全部支持并发操作 + // Thread-safe: no collections used, or all collections support concurrent access private boolean threadSafe; private int cacheSize; diff --git a/orm/src/main/java/com/zfoo/orm/model/IEntity.java b/orm/src/main/java/com/zfoo/orm/model/IEntity.java index 7de1ee9f..35b32274 100644 --- a/orm/src/main/java/com/zfoo/orm/model/IEntity.java +++ b/orm/src/main/java/com/zfoo/orm/model/IEntity.java @@ -19,7 +19,7 @@ public interface IEntity> { /** * EN: The primary key of a document. The type of primary key only supports int long float double String ObjectId - * CN: 一个文档的主键 primary key,主键Id支持的类型有6种 int long float double String ObjectId + * Document primary key. Supported types: int, long, float, double, String, ObjectId. */ PK id(); diff --git a/orm/src/main/java/com/zfoo/orm/model/IndexTextDef.java b/orm/src/main/java/com/zfoo/orm/model/IndexTextDef.java index 73689e48..7569a1f1 100644 --- a/orm/src/main/java/com/zfoo/orm/model/IndexTextDef.java +++ b/orm/src/main/java/com/zfoo/orm/model/IndexTextDef.java @@ -30,12 +30,12 @@ public class IndexTextDef { public IndexTextDef(Field field, IndexText indexText) { AssertionUtils.notNull(field); - // 是否被private修饰 + // Check if field is declared private if (!Modifier.isPrivate(field.getModifiers())) { - throw new IllegalArgumentException(StringUtils.format("[{}]没有被private修饰", field.getName())); + throw new IllegalArgumentException(StringUtils.format("[{}] is not declared private", field.getName())); } - // 唯一索引不能有set方法,为了避免客户端改变javabean中的属性 + // Unique index fields must not have setters, to prevent clients from modifying them Class clazz = field.getDeclaringClass(); AssertionUtils.notNull(clazz); this.field = field; diff --git a/orm/src/main/java/com/zfoo/orm/query/IQuery.java b/orm/src/main/java/com/zfoo/orm/query/IQuery.java index 19223a82..48345b2f 100644 --- a/orm/src/main/java/com/zfoo/orm/query/IQuery.java +++ b/orm/src/main/java/com/zfoo/orm/query/IQuery.java @@ -16,7 +16,7 @@ package com.zfoo.orm.query; import com.zfoo.orm.model.IEntity; /** - * 对数据库进行(查找)的相关方法 + * Methods for querying the database * * @author godotg */ diff --git a/orm/src/main/java/com/zfoo/orm/query/IQueryBuilder.java b/orm/src/main/java/com/zfoo/orm/query/IQueryBuilder.java index f023082c..43b13aba 100644 --- a/orm/src/main/java/com/zfoo/orm/query/IQueryBuilder.java +++ b/orm/src/main/java/com/zfoo/orm/query/IQueryBuilder.java @@ -24,40 +24,40 @@ import java.util.List; */ public interface IQueryBuilder, E extends IEntity> { - // EQ、= 等于(=) + // EQ = equal to IQueryBuilder eq(String fieldName, Object fieldValue); - // NE、<> 不等于(<>) + // NE <> not equal to IQueryBuilder ne(String fieldName, Object fieldValue); IQueryBuilder in(String fieldName, List fieldValueList); - // [n] in (不在)IN 查询 + // [n] IN / NOT IN query IQueryBuilder nin(String fieldName, List fieldValueList); - // LT、< 小于(<) + // LT < less than IQueryBuilder lt(String fieldName, Object fieldValue); - // lte、<= 小于等于(<=) + // LTE <= less than or equal to IQueryBuilder lte(String fieldName, Object fieldValue); - // GT、> 大于(>) + // GT > greater than IQueryBuilder gt(String fieldName, Object fieldValue); - // GTE、>= 大于等于(>=) + // GTE >= greater than or equal to IQueryBuilder gte(String fieldName, Object fieldValue); - // LIKE 模糊查询 + // LIKE fuzzy/regex query IQueryBuilder like(String fieldName, String fieldValue); List queryAll(); /** - * 分页查询,默认按照id排序 + * Paginated query; sorted by id by default * - * @param page 第几页 - * @param itemsPerPage 每页容量 + * @param page page number (1-based) + * @param itemsPerPage items per page */ Pair> queryPage(int page, int itemsPerPage); diff --git a/orm/src/main/java/com/zfoo/orm/query/Page.java b/orm/src/main/java/com/zfoo/orm/query/Page.java index b6bab111..afee6850 100644 --- a/orm/src/main/java/com/zfoo/orm/query/Page.java +++ b/orm/src/main/java/com/zfoo/orm/query/Page.java @@ -25,28 +25,28 @@ import java.util.List; public class Page { /** - * 第几页 + * page number (1-based) */ private int page; /** - * 页容量 + * page size */ private int itemsPerPage; /** - * 数据库记录总数量 + * total record count */ private long totalSize; public static Page valueOf(int page, int itemsPerPage, long totalSize) { if (page <= 0) { - throw new IllegalArgumentException(StringUtils.format("分页数必须大于0,[page:{}]", page)); + throw new IllegalArgumentException(StringUtils.format("page number must be greater than 0, [page:{}]", page)); } if (itemsPerPage <= 0) { - throw new IllegalArgumentException(StringUtils.format("页容量必须大于0,[size:{}]", itemsPerPage)); + throw new IllegalArgumentException(StringUtils.format("page size must be greater than 0, [size:{}]", itemsPerPage)); } if (totalSize < 0) { - throw new IllegalArgumentException(StringUtils.format("总数两必须大于等于0,[size:{}]", totalSize)); + throw new IllegalArgumentException(StringUtils.format("totalSize must be >= 0, [size:{}]", totalSize)); } var p = new Page(); p.page = page; diff --git a/orm/src/main/java/com/zfoo/orm/schema/OrmDefinitionParser.java b/orm/src/main/java/com/zfoo/orm/schema/OrmDefinitionParser.java index cf0100d0..3b0f46f8 100644 --- a/orm/src/main/java/com/zfoo/orm/schema/OrmDefinitionParser.java +++ b/orm/src/main/java/com/zfoo/orm/schema/OrmDefinitionParser.java @@ -44,29 +44,29 @@ public class OrmDefinitionParser implements BeanDefinitionParser { String name; BeanDefinitionBuilder builder; - // 解析OrmConfig的配置 + // Parse OrmConfig configuration parseOrmConfig(element, parserContext); - // 注册OrmContext + // Register OrmContext clazz = OrmContext.class; name = StringUtils.uncapitalize(clazz.getSimpleName()); builder = BeanDefinitionBuilder.rootBeanDefinition(clazz); parserContext.getRegistry().registerBeanDefinition(name, builder.getBeanDefinition()); - // 注册OrmManager + // Register OrmManager clazz = OrmManager.class; name = StringUtils.uncapitalize(clazz.getSimpleName()); builder = BeanDefinitionBuilder.rootBeanDefinition(clazz); builder.addPropertyReference("ormConfig", OrmConfig.class.getCanonicalName()); parserContext.getRegistry().registerBeanDefinition(name, builder.getBeanDefinition()); - // 注册MongodbAccessor + // Register MongodbAccessor clazz = MongodbAccessor.class; name = StringUtils.uncapitalize(clazz.getSimpleName()); builder = BeanDefinitionBuilder.rootBeanDefinition(clazz); parserContext.getRegistry().registerBeanDefinition(name, builder.getBeanDefinition()); - // 注册MongodbQuery + // Register MongodbQuery clazz = MongodbQuery.class; name = StringUtils.uncapitalize(clazz.getSimpleName()); builder = BeanDefinitionBuilder.rootBeanDefinition(clazz); @@ -85,11 +85,11 @@ public class OrmDefinitionParser implements BeanDefinitionParser { parseHostConfig(DomUtils.getFirstChildElementByTagName(element, "host"), parserContext); builder.addPropertyReference("host", HostConfig.class.getCanonicalName()); - // 解析caches标签 + // Parse 'caches' tag var caches = parseCacheStrategies(DomUtils.getFirstChildElementByTagName(element, "caches"), parserContext); builder.addPropertyValue("caches", caches); - // 解析persisters标签 + // Parse 'persisters' tag var persisters = parsePersisterStrategies(DomUtils.getFirstChildElementByTagName(element, "persisters"), parserContext); builder.addPropertyValue("persisters", persisters); @@ -98,7 +98,7 @@ public class OrmDefinitionParser implements BeanDefinitionParser { private void parseHostConfig(Element element, ParserContext parserContext) { - // 解析host标签 + // Parse 'host' tag var clazz = HostConfig.class; var builder = BeanDefinitionBuilder.rootBeanDefinition(clazz); diff --git a/orm/src/main/java/com/zfoo/orm/util/MongoIdUtils.java b/orm/src/main/java/com/zfoo/orm/util/MongoIdUtils.java index 60bed94f..b46db2d3 100644 --- a/orm/src/main/java/com/zfoo/orm/util/MongoIdUtils.java +++ b/orm/src/main/java/com/zfoo/orm/util/MongoIdUtils.java @@ -21,7 +21,6 @@ import com.zfoo.protocol.collection.CollectionUtils; import com.zfoo.protocol.util.AssertionUtils; import com.zfoo.protocol.util.StringUtils; import org.bson.Document; -import org.bson.conversions.Bson; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,15 +43,15 @@ public abstract class MongoIdUtils { /** - * 分布式唯一Id生成器,利用MongoDB数据库存储自增的ID,可以保证原子性,一致性。 + * Distributed unique ID generator backed by MongoDB auto-increment; guarantees atomicity and consistency. *

- * 线程安全,不同线程互不影响。 + * Thread-safe: independent across threads. *

- * 进程安全,不同的应用进程也可以保证唯一id。 + * Process-safe: unique IDs guaranteed across different application processes. * - * @param collectionName 存储的集合名称 - * @param documentName 文档id - * @return 增加后的唯一id + * @param collectionName collection name for storage + * @param documentName document ID name + * @return the incremented unique ID */ public static long getIncrementIdFromMongo(String collectionName, String documentName) { var collection = OrmContext.getOrmManager().getCollection(collectionName); @@ -69,7 +68,7 @@ public abstract class MongoIdUtils { var query = Filters.eq("_id", documentName); var inc = new Document("$inc", new Document(COUNT, 1L)); var setOnInsert = new Document("$setOnInsert", new Document("_id", documentName)); - // 报错后重试创建并获取id,在大并发create的时候mongodb总是会报错一次“duplicate key error!” 所以重试一次 + // Retry once on error; under high concurrency MongoDB may report "duplicate key error!" once for (var i = 0; i < 3; i++) { try { var document = collection.findOneAndUpdate(query, Updates.combine(inc, setOnInsert), new FindOneAndUpdateOptions().upsert(true)); @@ -94,9 +93,9 @@ public abstract class MongoIdUtils { // ---------------------------------------------------------------------------------------------------- /** - * 重置documentName的数值,重置为0 + * Reset the document counter to 0 * - * @param documentName 档id + * @param documentName document ID name */ public static void resetIncrementIdFromMongoDefault(String documentName) { setIncrementIdFromMongo(COLLECTION_NAME, documentName, 0L); @@ -124,11 +123,11 @@ public abstract class MongoIdUtils { // ---------------------------------------------------------------------------------------------------- /** - * 获取最大的id + * Get the current maximum ID * - * @param collectionName 存储的集合名称 - * @param documentName 文档id - * @return 增加后的唯一id + * @param collectionName collection name for storage + * @param documentName document ID name + * @return the incremented unique ID */ public static long getMaxIdFromMongo(String collectionName, String documentName) { var collection = OrmContext.getOrmManager().getCollection(collectionName); diff --git a/orm/src/test/java/com/zfoo/orm/accessor/AccessorTest.java b/orm/src/test/java/com/zfoo/orm/accessor/AccessorTest.java index 815a7edc..9b3c1b92 100644 --- a/orm/src/test/java/com/zfoo/orm/accessor/AccessorTest.java +++ b/orm/src/test/java/com/zfoo/orm/accessor/AccessorTest.java @@ -70,7 +70,7 @@ public class AccessorTest { } - // 批量插入 + // Batch insert public void batchInsert() { var list = new ArrayList(); for (var i = 1; i <= 10; i++) { diff --git a/orm/src/test/java/com/zfoo/orm/accessor/TransactionTest.java b/orm/src/test/java/com/zfoo/orm/accessor/TransactionTest.java index ea330277..fde164fd 100644 --- a/orm/src/test/java/com/zfoo/orm/accessor/TransactionTest.java +++ b/orm/src/test/java/com/zfoo/orm/accessor/TransactionTest.java @@ -25,7 +25,7 @@ import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; /** - * 必须要用复制集群才能使用事务 + * Transactions require a replica set cluster * * @author godotg */ diff --git a/orm/src/test/java/com/zfoo/orm/cache/EntityCachesTest.java b/orm/src/test/java/com/zfoo/orm/cache/EntityCachesTest.java index 5a37b0aa..114b03d5 100644 --- a/orm/src/test/java/com/zfoo/orm/cache/EntityCachesTest.java +++ b/orm/src/test/java/com/zfoo/orm/cache/EntityCachesTest.java @@ -38,10 +38,10 @@ public class EntityCachesTest { batchDelete(); - // 再插入 + // Insert again batchInsert(); - // 动态去拿到UserEntity的EntityCaches + // Dynamically retrieve EntityCaches for UserEntity @SuppressWarnings("unchecked") var userEntityCaches = (IEntityCache) OrmContext.getOrmManager().getEntityCaches(UserEntity.class); @@ -68,7 +68,7 @@ public class EntityCachesTest { @SuppressWarnings("unchecked") var userEntityCaches = (IEntityCache) OrmContext.getOrmManager().getEntityCaches(UserEntity.class); - // 动态去拿到UserEntity的EntityCaches + // Dynamically retrieve EntityCaches for UserEntity for (var i = 1; i <= 10; i++) { for (var j = 1; j <= 10; j++) { var entity = userEntityCaches.loadOrCreate((long) j); diff --git a/orm/src/test/java/com/zfoo/orm/cache/WrapperTest.java b/orm/src/test/java/com/zfoo/orm/cache/WrapperTest.java index c6f2a943..ca26fe03 100644 --- a/orm/src/test/java/com/zfoo/orm/cache/WrapperTest.java +++ b/orm/src/test/java/com/zfoo/orm/cache/WrapperTest.java @@ -34,7 +34,7 @@ public class WrapperTest { public void wrapperTest() { var context = new ClassPathXmlApplicationContext("application.xml"); - // 每次运行前先删除数据库 + // Clear database before each test run OrmContext.getOrmManager().getCollection(IntEntity.class).drop(); OrmContext.getOrmManager().getCollection(LongEntity.class).drop(); OrmContext.getOrmManager().getCollection(FloatEntity.class).drop(); diff --git a/orm/src/test/java/com/zfoo/orm/client/SingleMongoTest.java b/orm/src/test/java/com/zfoo/orm/client/SingleMongoTest.java index 8c10d16e..6aee9e28 100644 --- a/orm/src/test/java/com/zfoo/orm/client/SingleMongoTest.java +++ b/orm/src/test/java/com/zfoo/orm/client/SingleMongoTest.java @@ -18,7 +18,6 @@ import java.util.Arrays; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; import java.util.regex.Pattern; import static com.mongodb.client.model.Filters.eq; @@ -47,12 +46,12 @@ public class SingleMongoTest { System.out.println("Connect to database successfully!"); System.out.println("MongoDatabase inof is : " + mongodb.getName()); - System.out.println("当前数据库中的所有集合是:"); + System.out.println("All collections in current database:"); for (String name : mongodb.listCollectionNames()) { System.out.println(name); } - // 查找并且遍历集合student的所有文档 + // Find and iterate all documents in collection 'student' MongoCollection collection = mongodb.getCollection("student"); System.out.println("Collection created successfully"); collection.find().forEach(document -> System.out.println(document)); @@ -75,7 +74,7 @@ public class SingleMongoTest { @Test public void aggregateTest() { var collection = mongodb.getCollection("student"); - // likeNum中有9的文档根据age分组后统计数量。 + // Group documents where 'likeNum' contains 9 by age and count var result = collection.aggregate(Arrays.asList( Aggregates.match(Filters.eq("name", "jay1")), Aggregates.group("$age", Accumulators.sum("count", 1))) @@ -86,10 +85,10 @@ public class SingleMongoTest { @Test public void insertOneTest() { - // 查找并且便利集合student的所有文档 + // Find and iterate all documents in collection 'student' MongoCollection collection = mongodb.getCollection("student"); - // 插入一个文档 + // Insert a document Document document = new Document("_id", 1) .append("name", "hello mongodb"); collection.insertOne(document); @@ -99,10 +98,10 @@ public class SingleMongoTest { @Test public void insertManyTest() { - // 查找并且便利集合student的所有文档 + // Find and iterate all documents in collection 'student' MongoCollection collection = mongodb.getCollection("student"); - //插入数据 + // Insert data var datas = new ArrayList(); for (int i = 0; i < 10; i++) { var studentName = "jay" + i; @@ -110,17 +109,17 @@ public class SingleMongoTest { datas.add(document); } collection.insertMany(datas); - // 查询年龄大于等于20,小于25 + // Query: age >= 20 and age < 25 FindIterable result = collection.find(new Document("age", new Document("$gte", 20).append("$lt", 25))); System.out.println(result.first()); } @Test public void updateOneTest() { - // 查找并且便利集合student的所有文档 + // Find and iterate all documents in collection 'student' MongoCollection collection = mongodb.getCollection("student"); - // 更新一个文档,updateOne方法第一个参数是查询条件,如果查出多条也只修改第一条;第二个参数是修改条件。 + // Update one document; first arg is filter, second is update (only modifies first match) collection.updateOne(eq("age", 10), new Document("$set", new Document("name", "new hello mongodb"))); collection.find().forEach(doc -> System.out.println(doc.toJson())); @@ -129,10 +128,10 @@ public class SingleMongoTest { @Test public void updateManyTest() { - // 查找并且便利集合student的所有文档 + // Find and iterate all documents in collection 'student' MongoCollection collection = mongodb.getCollection("student"); - // 更新一个文档 + // Update one document collection.updateMany(eq("age", 10), new Document("$set", new Document("name", "new hello mongodb"))); collection.find().forEach(doc -> System.out.println(doc.toJson())); @@ -142,7 +141,7 @@ public class SingleMongoTest { public void deleteTest() { MongoCollection collection = mongodb.getCollection("student"); - // 删除一个文档 + // Delete one document collection.deleteOne(eq("_id", 1)); collection.find().forEach(doc -> System.out.println(doc.toJson())); @@ -151,10 +150,10 @@ public class SingleMongoTest { // ****************************************************************************************************** - // 更新一个字段和更新多个字段测试,测试结果,区别不大,可以忽略 + // Test updating one field vs multiple fields; results show negligible difference - // 耗时[42598] + // time: ~42598ms @Test public void updateAllFieldTest() { var startTime = TimeUtils.currentTimeMillis(); @@ -170,7 +169,7 @@ public class SingleMongoTest { var key = j; var value = j; try { - // 插入一个文档 + // Insert a document Document document = new Document("_id", key) .append("name0", value) .append("name1", value) @@ -192,10 +191,10 @@ public class SingleMongoTest { var endTime = TimeUtils.currentTimeMillis(); - System.out.println(StringUtils.format("耗时[{}]", endTime - startTime)); + System.out.println(StringUtils.format("elapsed[{}]ms", endTime - startTime)); } - // 耗时[41971] + // time: ~41971ms @Test public void updateOneFieldTest() { var startTime = TimeUtils.currentTimeMillis(); @@ -210,7 +209,7 @@ public class SingleMongoTest { var key = j; var value = j; try { - // 插入一个文档 + // Insert a document Document document = new Document("_id", key) .append("name0", value); collection.updateOne(eq("_id", key), new Document("$set", document)); @@ -221,30 +220,30 @@ public class SingleMongoTest { var endTime = TimeUtils.currentTimeMillis(); - System.out.println(StringUtils.format("耗时[{}]", endTime - startTime)); + System.out.println(StringUtils.format("elapsed[{}]ms", endTime - startTime)); } // ****************************************************************************************************** - // wiredTiger引擎测试 - // inMemory引擎测试 + // WiredTiger engine test + // InMemory engine test private static final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 10); /** - * inMemory测试结果如下: - * 总线程数[1],操作数[10000],耗时[4212] - * 总线程数[1],操作数[1000000],耗时[394215] - * 总线程数[4],操作数[1000000],耗时[223355] - * 总线程数[8],操作数[1000000],耗时[297476] - * 总线程数[16],操作数[1000000],耗时[442782] - * 总线程数[32],操作数[1000000],耗时[713675] + * InMemory engine benchmark results: + * threads[1], ops[10000], elapsed[4212]ms + * threads[1], ops[1000000], elapsed[394215]ms + * threads[4], ops[1000000], elapsed[223355]ms + * threads[8], ops[1000000], elapsed[297476]ms + * threads[16], ops[1000000], elapsed[442782]ms + * threads[32], ops[1000000], elapsed[713675]ms *

*

- * wiredTiger测试结果如下: - * 总线程数[1],操作数[10000],耗时[4170] - * 总线程数[1],操作数[1000000],耗时[400208] - * 总线程数[4],操作数[1000000],耗时[230471] - * 总线程数[8],操作数[1000000],耗时[298409] + * WiredTiger engine benchmark results: + * threads[1], ops[10000], elapsed[4170]ms + * threads[1], ops[1000000], elapsed[400208]ms + * threads[4], ops[1000000], elapsed[230471]ms + * threads[8], ops[1000000], elapsed[298409]ms */ @Test public void mongodbWriteTest() { @@ -269,7 +268,7 @@ public class SingleMongoTest { var key = j; var value = j; try { - // 插入一个文档 + // Insert a document Document document = new Document("_id", key) .append("name", value); collection.updateOne(eq("_id", key), new Document("$set", document)); @@ -293,18 +292,18 @@ public class SingleMongoTest { } var endTime = TimeUtils.currentTimeMillis(); - System.out.println(StringUtils.format("总线程数[{}],操作数[{}],耗时[{}]" + System.out.println(StringUtils.format("threads[{}], ops[{}], elapsed[{}]ms" , threadNum, count, endTime - startTime)); } /** - * 总线程数[1],操作数[10000],耗时[4252] - * 总线程数[1],操作数[1000000],耗时[407131] - * 总线程数[4],操作数[1000000],耗时[226040] - * 总线程数[8],操作数[1000000],耗时[297418] - * 总线程数[16],操作数[1000000],耗时[436799] - * 总线程数[32],操作数[1000000],耗时[716651] + * threads[1], ops[10000], elapsed[4252]ms + * threads[1], ops[1000000], elapsed[407131]ms + * threads[4], ops[1000000], elapsed[226040]ms + * threads[8], ops[1000000], elapsed[297418]ms + * threads[16], ops[1000000], elapsed[436799]ms + * threads[32], ops[1000000], elapsed[716651]ms */ @Test public void mongodbReadTest() { @@ -351,7 +350,7 @@ public class SingleMongoTest { } var endTime = TimeUtils.currentTimeMillis(); - System.out.println(StringUtils.format("总线程数[{}],操作数[{}],耗时[{}]" + System.out.println(StringUtils.format("threads[{}], ops[{}], elapsed[{}]ms" , threadNum, count, endTime - startTime)); } diff --git a/orm/src/test/java/com/zfoo/orm/entity/WrongEntity.java b/orm/src/test/java/com/zfoo/orm/entity/WrongEntity.java index a8dd9056..e1c4f72c 100644 --- a/orm/src/test/java/com/zfoo/orm/entity/WrongEntity.java +++ b/orm/src/test/java/com/zfoo/orm/entity/WrongEntity.java @@ -2,12 +2,11 @@ package com.zfoo.orm.entity; import com.zfoo.orm.anno.EntityCache; import com.zfoo.orm.anno.Id; -import com.zfoo.orm.anno.Index; import com.zfoo.orm.model.IEntity; import org.bson.codecs.pojo.annotations.BsonId; /** - * 用法有问题的entity + * Entity with intentionally wrong usage (for testing validation) * * @author Sando * @version 1.0 diff --git a/orm/src/test/java/com/zfoo/orm/text/IndexTextTest.java b/orm/src/test/java/com/zfoo/orm/text/IndexTextTest.java index 69d09bdb..cb98d35c 100644 --- a/orm/src/test/java/com/zfoo/orm/text/IndexTextTest.java +++ b/orm/src/test/java/com/zfoo/orm/text/IndexTextTest.java @@ -34,12 +34,12 @@ public class IndexTextTest { public void insertTest() { var context = new ClassPathXmlApplicationContext("application.xml"); var listUser = new ArrayList(); - var userEntity = new UserEntity(1, (byte) 1, (short) 1, 1, true, "两个黄鹂鸣翠柳,一行白鹭上青天。窗含西岭千秋雪,门泊东吴万里船。", null); + var userEntity = new UserEntity(1, (byte) 1, (short) 1, 1, true, "Two orioles sing among the green willows, a row of egrets fly into the blue sky.", null); listUser.add(userEntity); - userEntity = new UserEntity(2, (byte) 2, (short) 2, 2, true, "床前明月光,疑是地上霜。 举头望明月,低头思故乡。", null); + userEntity = new UserEntity(2, (byte) 2, (short) 2, 2, true, "Moonlight before my bed, could be frost on the ground.", null); listUser.add(userEntity); - userEntity = new UserEntity(3, (byte) 3, (short) 3, 3, true, "吴丝蜀桐张高秋,空山凝云颓不流。 江娥啼竹素女愁,李凭中国弹箜篌。 昆山玉碎凤凰叫,芙蓉泣露香兰笑。 十二门前融冷光,二十三丝动紫皇" + - "。 女娲炼石补天处,石破天惊逗秋雨。 梦入神山教神妪,老鱼跳波瘦蛟舞。 吴质不眠倚桂树,露脚斜飞湿寒兔。", null); + userEntity = new UserEntity(3, (byte) 3, (short) 3, 3, true, "Ancient Chinese instrument music poem test string" + + " Ancient music metaphor continued.", null); listUser.add(userEntity); OrmContext.getAccessor().batchInsert(listUser); ThreadUtils.sleep(Long.MAX_VALUE); @@ -49,7 +49,7 @@ public class IndexTextTest { public void queryTest() { var context = new ClassPathXmlApplicationContext("application.xml"); var collection = OrmContext.getOrmManager().getCollection(UserEntity.class); - collection.find(Filters.text("窗含西岭千秋雪")).forEach(new Consumer() { + collection.find(Filters.text("orioles")).forEach(new Consumer() { @Override public void accept(UserEntity userEntity) { System.out.println(userEntity); diff --git a/orm/src/test/java/com/zfoo/orm/util/MongoUuidUtilsTest.java b/orm/src/test/java/com/zfoo/orm/util/MongoUuidUtilsTest.java index a58253a7..d543ef95 100644 --- a/orm/src/test/java/com/zfoo/orm/util/MongoUuidUtilsTest.java +++ b/orm/src/test/java/com/zfoo/orm/util/MongoUuidUtilsTest.java @@ -19,7 +19,7 @@ import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; /** - * MongoDB分布式唯一Id测试 + * Test for MongoDB distributed unique ID generator * * @author godotg */