Java 中的单元工作模式:协调高效的事务管理
大约 4 分钟
单元工作设计模式的意图
了解 Java 单元工作模式如何巧妙地管理和维护受业务事务影响的对象列表,有效地协调数据库更改并解决并发问题。
单元工作模式的详细说明,以及现实世界的示例
现实世界示例
考虑一个图书馆场景,其中图书馆员仔细跟踪所有借出和归还的书籍,利用单元工作设计模式来高效地更新库存系统。图书馆员不会在每次发生单个交易时更新图书馆的库存系统,而是会维护一个包含所有更改的列表,并在一天结束时一次性更新系统。这种方法确保所有更改一起处理,维护库存的完整性并减少所需的单个更新次数。这类似于软件中的单元工作模式,其中所有对一组对象的更改都被跟踪,并在单个事务中提交以维护一致性和效率。
简单来说
单元工作模式跟踪事务期间对对象的更改,并将所有更改作为一个单元提交,以确保一致性和效率。
维护受业务事务影响的对象列表,并协调更改的写出和并发问题的解决。
Java 中单元工作模式的编程示例
武器经销商有一个包含武器信息的数据库。城里各地的商人不断更新这些信息,导致数据库服务器负载过高。为了使负载更易于管理,我们将单元工作模式应用于以批处理方式发送许多小的更新。
以下是正在数据库中持久化的Weapon
实体。
@Getter
@RequiredArgsConstructor
public class Weapon {
private final Integer id;
private final String name;
}
实现的本质是ArmsDealer
,它实现了单元工作模式。它维护一个需要执行的数据库操作映射(context
),当调用commit
时,它会以批处理方式应用这些操作。
public interface IUnitOfWork<T> {
String INSERT = "INSERT";
String DELETE = "DELETE";
String MODIFY = "MODIFY";
void registerNew(T entity);
void registerModified(T entity);
void registerDeleted(T entity);
void commit();
}
@Slf4j
@RequiredArgsConstructor
public class ArmsDealer implements IUnitOfWork<Weapon> {
private final Map<String, List<Weapon>> context;
private final WeaponDatabase weaponDatabase;
@Override
public void registerNew(Weapon weapon) {
LOGGER.info("Registering {} for insert in context.", weapon.getName());
register(weapon, UnitActions.INSERT.getActionValue());
}
@Override
public void registerModified(Weapon weapon) {
LOGGER.info("Registering {} for modify in context.", weapon.getName());
register(weapon, UnitActions.MODIFY.getActionValue());
}
@Override
public void registerDeleted(Weapon weapon) {
LOGGER.info("Registering {} for delete in context.", weapon.getName());
register(weapon, UnitActions.DELETE.getActionValue());
}
private void register(Weapon weapon, String operation) {
var weaponsToOperate = context.get(operation);
if (weaponsToOperate == null) {
weaponsToOperate = new ArrayList<>();
}
weaponsToOperate.add(weapon);
context.put(operation, weaponsToOperate);
}
@Override
public void commit() {
if (context == null || context.isEmpty()) {
return;
}
LOGGER.info("Commit started");
if (context.containsKey(UnitActions.INSERT.getActionValue())) {
commitInsert();
}
if (context.containsKey(UnitActions.MODIFY.getActionValue())) {
commitModify();
}
if (context.containsKey(UnitActions.DELETE.getActionValue())) {
commitDelete();
}
LOGGER.info("Commit finished.");
}
private void commitInsert() {
var weaponsToBeInserted = context.get(UnitActions.INSERT.getActionValue());
for (var weapon : weaponsToBeInserted) {
LOGGER.info("Inserting a new weapon {} to sales rack.", weapon.getName());
weaponDatabase.insert(weapon);
}
}
private void commitModify() {
var modifiedWeapons = context.get(UnitActions.MODIFY.getActionValue());
for (var weapon : modifiedWeapons) {
LOGGER.info("Scheduling {} for modification work.", weapon.getName());
weaponDatabase.modify(weapon);
}
}
private void commitDelete() {
var deletedWeapons = context.get(UnitActions.DELETE.getActionValue());
for (var weapon : deletedWeapons) {
LOGGER.info("Scrapping {}.", weapon.getName());
weaponDatabase.delete(weapon);
}
}
}
以下是如何将整个应用程序组合在一起。
public static void main(String[] args) {
// create some weapons
var enchantedHammer = new Weapon(1, "enchanted hammer");
var brokenGreatSword = new Weapon(2, "broken great sword");
var silverTrident = new Weapon(3, "silver trident");
// create repository
var weaponRepository = new ArmsDealer(new HashMap<>(),
new WeaponDatabase());
// perform operations on the weapons
weaponRepository.registerNew(enchantedHammer);
weaponRepository.registerModified(silverTrident);
weaponRepository.registerDeleted(brokenGreatSword);
weaponRepository.commit();
}
以下是控制台输出。
21:39:21.984 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Registering enchanted hammer for insert in context.
21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Registering silver trident for modify in context.
21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Registering broken great sword for delete in context.
21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Commit started
21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Inserting a new weapon enchanted hammer to sales rack.
21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Scheduling silver trident for modification work.
21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Scrapping broken great sword.
21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Commit finished.
何时在 Java 中使用单元工作模式
- 单元工作模式非常适合管理必须作为单个事务执行的 Java 中的多个数据库操作,以确保数据一致性和完整性。
- 在必须以协调方式跟踪和保存对业务对象的更改的场景中很理想。
- 在使用 Java 中的诸如 Hibernate 之类的对象关系映射 (ORM) 框架时很有用。
单元工作模式 Java 教程
Java 中单元工作模式的实际应用
- Java 基于 ORM 框架(如 Hibernate)中的实现。
- 需要多个数据库操作原子的企业应用程序。
- 多个对象被修改并一起持久化的复杂事务系统。
单元工作模式的优点和权衡
优点
- 通过有效地管理事务来确保数据完整性。
- 通过将数据库调用一起批处理来减少数据库调用次数。
- 通过将事务管理与业务逻辑解耦来简化持久性逻辑。
权衡
- 在管理单元工作中对象的生命周期时可能会引入复杂性。
- 如果管理不当,尤其是对于大型数据集,可能会出现性能开销。
相关的 Java 设计模式
- 标识映射:有助于确保每个对象在每个事务中仅加载一次,从而减少冗余并提高性能。
- 存储库:通常与单元工作一起使用,以抽象持久性逻辑并提供更简洁的数据访问方式。
- 事务脚本:虽然其过程方法不同,但它可以通过在更高层次上管理事务逻辑来补充单元工作。