tp5模型能实现事务吗

Even in the most basic scenario you can picture, where the logic of an application’s core is boiled down to just pulling in a few records from the database, bringing some domain objects to life, and then dumping them to the screen through an API of some basic rendering mechanism, there’s always an ongoing transaction behind the scenes whose most expensive facet often gets blurred beneath the appealing outward influence of the user interface.

即使在最基本的情况下,您也可以想象,其中应用程序核心的逻辑可以归结为仅从数据库中提取一些记录,使某些域对象变为现实,然后通过某些API将它们转储到屏幕上。基本的渲染机制,总是在幕后进行交易,其最昂贵的方面通常会在用户界面吸引人的外部影响下变得模糊。

If you think this through, you’ll notice that the crux of the matter is the transaction lies not surprisingly in the heap of database trips, even though they can be largely mitigated by a clever caching strategy. In relatively small applications, where there are just a few basic domain objects involved in each transaction, and where the hike to the database is just for retrieving data most of the time, a simple caching system dropped into the appropriate place certainly can help get things sorted out with efficiency.

如果您仔细考虑一下,您会发现问题的症结在于事务并不奇怪地出现在数据库行程堆中,即使可以通过聪明的缓存策略在很大程度上缓解它们。 在相对较小的应用程序中,每个事务只涉及几个基本域对象,并且大多数时候到数据库只是为了检索数据而已,将简单的缓存系统放在适当的位置肯定可以帮助解决问题高效地整理。

While sad but true, reality is a ruthless creature always shouting at us radically different things than the sweet ones we’d rather hear instead. In most cases, because of the intrinsic, unavoidable mutability of domain objects (with a few scarce exceptions when the dependencies of domain classes are modelled around the concept of immutable Value Objects), chances are that some objects will need to be modified across multiple requests, and even new ones will be put in memory in response to some user-related event.

现实虽然悲伤但真实,但却是一个残酷的动物,总是向我们大喊大叫,而不是我们宁愿听到的甜蜜的事情。 在大多数情况下,由于领域对象固有的,不可避免的可变性(当围绕不变类型的值对象的概念对领域类的依赖关系进行建模时,少数稀有例外),有可能需要跨多个请求修改某些对象,甚至新的内容也会响应某些与用户相关的事件而被保存在内存中。

In short, this means that even dummy CRUD applications that don’t encapsulate extensive chunks of additional business logic can quickly become bloated and generate a lot of overhead under the hood when it comes to performing multiple database writes. What if they reach a point where it’s necessary to handle a huge number of domain objects, which must be persisted and removed in sync, without compromising what us programming plebs loosely call data integrity?

简而言之,这意味着即使不封装大量附加业务逻辑块的虚拟CRUD应用程序也可能很快变得肿,并在执行多个数据库写入时在后台产生大量开销。 如果它们达到了必须处理大量域对象(必须在不牺牲我们编程能力的前提下同步保存和同步)的程度,那又该怎么办呢?

Let’s be honest with ourselves (at least once). Neither all the lofty data source architectural patterns that we could just pick up along the way, nor that cool new approach we might have figured out overnight, can tackle satisfactorily something as predictable and mundane as writing out and removing multiple sets of data from storage. In light of this, should we just give up and call the issue pretty much a lost cause?

让我们对自己诚实(至少一次)。 我们一路走来都无法掌握的所有崇高的数据源架构模式,或者我们可能在一夜之间就想出的超酷新方法,都无法令人满意地解决诸如从存储中写出和删除多组数据之类的可预测且平凡的事情。 有鉴于此,我们是否应该放弃并把这个问题称为失败的原因?

Admittedly the question is rhetorical. In fact, it’s feasible to wrap collections of domain objects inside a fairly flexible business transactional model and just perform several database writes/deletes in one go, therefore avoiding having to break down the process into more atomic and expensive database calls, which always lead to the session-per-operation antipattern. Moreover, this transaction-based mechanism rests on the academic formalities of a design pattern commonly known as Unit of Work (UOW), and its implementation in several popular enterprise-level packages, such as Hibernate, is quite prolific and prosperous.

诚然,这个问题是修辞。 实际上,将域对象的集合包装在一个相当灵活的业务事务模型中并一次执行几次数据库写/删除操作是可行的,因此避免了将流程分解为更多原子和昂贵的数据库调用的麻烦,这总是导致每次操作会话反模式。 此外,这种基于事务的机制基于通常称为工作单元 (UOW)的设计模式的学术形式,并且在几种流行的企业级程序包(例如Hibernate )中实现该方法相当多产且兴旺。

On the flip side, PHP is, for obvious reasons, still elusive at having a variety of UOWs running in production, excepting in a few well-trusted libraries like Doctrine and RedBeanPHP, which use the pattern’s forces at disparate levels in order to process and coordinate operations on entities. Despite this, it would be certainly pretty educational to take a closer look at the benefits a UOW provides, that way you can see if they are something that may meet your requirements.

另一方面,出于明显的原因,PHP仍然难以在生产环境中运行各种UOW,除了一些受人信赖的库(例如Doctrine和RedBeanPHP)外 ,它们在不同的级别使用模式的作用力来进行处理和处理。协调实体上的操作。 尽管如此,仔细研究UOW所提供的好处无疑是很有教育意义的,这样您就可以查看它们是否可以满足您的要求。

用工作单元注册域对象 (Registering Domain Objects with a Unit of Work)

In his book Patterns of Enterprise Application Architecture, Martin Fowler discusses two mainstream approaches that can be followed when it comes to implementing a UOW: the first makes the UOW directly responsible for registering or queuing domain objects for insertion, update, or deletion, and the second shifts this responsibility over to the domain objects themselves.

Martin Fowler在他的《 企业应用程序体系结构模式》一书中讨论了实现UOW时可以遵循的两种主流方法:第一种使UOW直接负责注册或排队域对象以进行插入,更新或删除,以及第二,将责任转移到域对象本身。

In this case, since I’d like to have the domain model only encapsulating my business logic and remain agnostic about any form of persistence that may exist further down in other layers, I’m going to just stick to the commandments of the first option. In either case, you’re free to pick the approach you feel will fit the bill the best.

在这种情况下,由于我只想让域模型封装我的业务逻辑,并且对其他层中可能存在的任何形式的持久性保持不可知论,因此我将只遵循第一种选择的诫命。 无论哪种情况,您都可以自由选择最适合您的方法。

A lightweight implementation of a UOW might look like this:

UOW的轻量级实现可能看起来像这样:

<?php
namespace ModelRepository;
use ModelEntityInterface;
interface UnitOfWorkInterface
{
public function fetchById($id);
public function registerNew(EntityInterface $entity);
public function registerClean(EntityInterface $entity);
public function registerDirty(EntityInterface $entity);
public function registerDeleted(EntityInterface $entity);
public function commit();
public function rollback();
public function clear();
}
<?php
namespace ModelRepository;
use MapperDataMapperInterface,
LibraryStorageObjectStorageInterface,
ModelEntityInterface;
class UnitOfWork implements UnitOfWorkInterface
{
const STATE_NEW     = "NEW";
const STATE_CLEAN   = "CLEAN";
const STATE_DIRTY   = "DIRTY";
const STATE_REMOVED = "REMOVED";
protected $dataMapper;
protected $storage;
public function __construct(DataMapperInterface $dataMapper, ObjectStorageInterface $storage) {
$this->dataMapper = $dataMapper;
$this->storage = $storage;
}
public function getDataMapper() {
return $this->dataMapper;
}
public function getObjectStorage() {
return $this->storage;
}
public function fetchById($id) {
$entity = $this->dataMapper->fetchById($id);
$this->registerClean($entity);
return $entity;
}
public function registerNew(EntityInterface $entity) {
$this->registerEntity($entity, self::STATE_NEW);
return $this;
}
public function registerClean(EntityInterface $entity) {
$this->registerEntity($entity, self::STATE_CLEAN);
return $this;
}
public function registerDirty(EntityInterface $entity) {
$this->registerEntity($entity, self::STATE_DIRTY);
return $this;
}
public function registerDeleted(EntityInterface $entity) {
$this->registerEntity($entity, self::STATE_REMOVED);
return $this;
}
protected function registerEntity($entity, $state = self::STATE_CLEAN) {
$this->storage->attach($entity, $state);
}
public function commit() {
foreach ($this->storage as $entity) {
switch ($this->storage[$entity]) {
case self::STATE_NEW:
case self::STATE_DIRTY:
$this->dataMapper->save($entity);
break;
case self::STATE_REMOVED:
$this->dataMapper->delete($entity);
}
}
$this->clear();
}
public function rollback() {
// your custom rollback implementation goes here
}
public function clear() {
$this->storage->clear();
return $this;
}
}

It should be clear to see that a UOW is nothing but plain, in-memory object storage which keeps track of which domain objects should be scheduled for insertion, update, and removal. In short, the convention could be boiled down to something along these lines: domain objects that need to be added to the storage will be registered “NEW”; those being updated will be marked “DIRTY”; the ones flagged “REMOVED” will be… yep, dropped from the database. In addition, any object registered “CLEAN” will be kept frozen and safe in memory until the client code explicitly requests to modify its associated state.

应该清楚地看到,一个UOW只是普通的内存中对象存储,它跟踪应该为插入,更新和删除计划哪些域对象。 简而言之,可以将约定归结为以下几类:需要添加到存储中的域对象将被注册为“ NEW”; 那些被更新的将被标记为“ DIRTY”; 标记为“已删除”的将是……是的,已从数据库中删除。 此外,任何注册为“ CLEAN”的对象都将被冻结并安全保存在内存中,直到客户端代码明确请求修改其关联状态为止。

Of course, the method that performs these persistence-related operations in just one single transaction is commit(), which exploits the functionality of an still undefined data mapper to get access to the persistence layer. It would be even easier for you to understand the UOW’s inner workings if I show you the implementation of the collaborators injected in its constructor, so here’s the components that compose the object storage module:

当然,仅在一个事务中执行这些与持久性相关的操作的方法就是commit() ,它利用仍未定义的数据映射器的功能来访问持久层。 如果我向您展示在其构造函数中注入的协作者的实现,您将更容易理解UOW的内部工作原理,因此以下是组成对象存储模块的组件:

<?php
namespace LibraryStorage;
interface ObjectStorageInterface extends Countable, Iterator, ArrayAccess
{
public function attach($object, $data = null);
public function detach($object);
public function clear();
}
<?php
namespace LibraryStorage;
class ObjectStorage extends SplObjectStorage implements ObjectStorageInterface
{
public function clear() {
$tempStorage = clone $this;
$this->addAll($tempStorage);
$this->removeAll($tempStorage);
$tempStorage = null;
}
}

In this case in particular, I decided to use a slightly-customized implementation of the SplObjectStorage class for registering domain objects without much fuss along with their related states with the UOW, even though pretty much the same can be also achieved using plain arrays. Again, it’s up to you to have the domain objects registered by using the method that best accommodates your needs.

尤其是在这种情况下,我决定使用SplObjectStorage类的稍微自定义的实现来注册域对象,而不必大惊小怪,以及它们与UOW的相关状态,即使使用普通数组也可以实现几乎相同的效果。 同样,由您使用最能满足您需要的方法来注册域对象。

With the custom ObjectStorage class in place, let’s take a look at the implementation of the aforementioned data mapper:

放置好自定义ObjectStorage类后,让我们看一下上述数据映射器的实现:

<?php
namespace Mapper;
use ModelEntityInterface;
interface DataMapperInterface
{
public function fetchById($id);
public function fetchAll(array $conditions = array());
public function insert(EntityInterface $entity);
public function update(EntityInterface $entity);
public function save(EntityInterface $entity);
public function delete(EntityInterface $entity);
}
<?php
namespace Mapper;
use LibraryDatabaseDatabaseAdapterInterface,
ModelCollectionEntityCollectionInterface,
ModelEntityInterface;
abstract class AbstractDataMapper implements DataMapperInterface
{
protected $adapter;
protected $collection;
protected $entityTable;
public function __construct(DatabaseAdapterInterface $adapter, EntityCollectionInterface $collection, $entityTable = null) {
$this->adapter = $adapter;
$this->collection = $collection;
if ($entityTable !== null) {
$this->setEntityTable($entityTable);
}
}
public function setEntityTable($entityTable) {
if (!is_string($table) || empty($entityTable)) {
throw new InvalidArgumentException(
"The entity table is invalid.");
}
$this->entityTable = $entityTable;
return $this;
}
public function fetchById($id) {
$this->adapter->select($this->entityTable,
array("id" => $id));
if (!$row = $this->adapter->fetch()) {
return null;
}
return $this->loadEntity($row);
}
public function fetchAll(array $conditions = array()) {
$this->adapter->select($this->entityTable, $conditions);
$rows = $this->adapter->fetchAll();
return $this->loadEntityCollection($rows);
}
public function insert(EntityInterface $entity) {
return $this->adapter->insert($this->entityTable,
$entity->toArray());
}
public function update(EntityInterface $entity) {
return $this->adapter->update($this->entityTable,
$entity->toArray(), "id = $entity->id");
}
public function save(EntityInterface $entity) {
return !isset($entity->id)
? $this->adapter->insert($this->entityTable,
$entity->toArray())
: $this->adapter->update($this->entityTable,
$entity->toArray(), "id = $entity->id");
}
public function delete(EntityInterface $entity) {
return $this->adapter->delete($this->entityTable,
"id = $entity->id");
}
protected function loadEntityCollection(array $rows) {
$this->collection->clear();
foreach ($rows as $row) {
$this->collection[] = $this->loadEntity($row);
}
return $this->collection;
}
abstract protected function loadEntity(array $row);
}

The AbstractDataMapper puts behind a pretty standard API the bulk of logic required for pulling domain objects in and out of the database. To make things even easier, it’d be also nice to derivate a refined implementation of it, that way we could easily test the UOW with a few sample user objects. Here’s how this extra mapping subclass looks:

AbstractDataMapper将相当标准的API放在后面,该逻辑是将域对象拉入和拉出数据库所需的大量逻辑。 为了使事情变得更容易,最好派生它的完善实现,这样我们可以用几个样本用户对象轻松地测试UOW。 这是这个额外的映射子类的外观:

<?php
namespace Mapper;
use ModelUser;
class UserMapper extends AbstractDataMapper
{
protected $entityTable = "users";
protected function loadEntity(array $row) {
return new User(array(
"id"    => $row["id"],
"name"  => $row["name"],
"email" => $row["email"],
"role"  => $row["role"]));
}
}

At this point we just could put our hands on the UOW and see if its transactional schema delivers what it promises. But before we do, first off we really should drop at least a few domain objects in memory. That way, we can get them neatly registered with the UOW. So let’s now define a primitive Domain Model which will be charged with supplying the objects in question.

在这一点上,我们可以把手放在UOW上,看看它的事务模式是否实现了它的承诺。 但是,在这样做之前,首先,我们确实应该在内存中至少删除几个域对象。 这样,我们就可以使它们在UOW上整齐地注册。 现在让我们定义一个原始的域模型 ,该模型将负责提供有问题的对象。

定义基本的领域模型 (Defining a basic Domain Model)

Frankly speaking, there are several ways to implement a functional Domain Model (most likely there exists one per developer living and breathing out there). Since in this case I want the process to be both painless and short, the model I’ll be using for testing the UOW will be composed just of a prototypical entity class, along with a derivative, which will be charged with spawning basic users objects:

坦白地说,有几种方法可以实现功能性的领域模型(每个开发人员很可能在其中生活和呼吸一个模型)。 因为在这种情况下,我希望过程既轻松又短,所以我将用于测试UOW的模型将仅由原型实体类以及派生类组成,派生类将负责产生基本用户对象:

<?php
namespace Model;
interface EntityInterface
{
public function setField($name, $value);
public function getField($name);
public function fieldExists($name);
public function removeField($name);
public function toArray();
}
<?php
namespace Model;
abstract class AbstractEntity implements EntityInterface
{
protected $fields = array();
protected $allowedFields = array();
public function __construct(array $fields = array()) {
if (!empty($fields)) {
foreach ($fields as $name => $value) {
$this->$name = $value;
}
}
}
public function setField($name, $value) {
return $this->__set($name, $value);
}
public function getField($name) {
return $this->__get($name);
}
public function fieldExists($name) {
return $this->__isset($name);
}
public function removeField($name) {
return $this->__unset($name);
}
public function toArray() {
return $this->fields;
}
public function __set($name, $value) {
$this->checkAllowedFields($name);
$mutator = "set" . ucfirst(strtolower($name));
if (method_exists($this, $mutator) &&
is_callable(array($this, $mutator))) {
$this->$mutator($value);
}
else {
$this->fields[$name] = $value;
}
return $this;
}
public function __get($name) {
$this->checkAllowedFields($name);
$accessor = "get" . ucfirst($name);
if (method_exists($this, $accessor) &&
is_callable(array($this, $accessor))) {
return $this->$accessor();
}
if (!$this->__isset($name)) {
throw new InvalidArgumentException(
"The field '$name' has not been set for this entity yet.");
}
return $this->fields[$name];
}
public function __isset($name) {
$this->checkAllowedFields($name);
return isset($this->fields[$name]);
}
public function __unset($name) {
$this->checkAllowedFields($name);
if (!$this->__isset($name)) {
throw new InvalidArgumentException(
"The field "$name" has not been set for this entity yet.");
}
unset($this->fields[$name]);
return $this;
}
protected function checkAllowedFields($field) {
if (!in_array($field, $this->allowedFields)) {
throw new InvalidArgumentException(
"The requested operation on the field '$field' is not allowed for this entity.");
}
}
}
<?php
namespace Model;
class User extends AbstractEntity
{
const ADMINISTRATOR_ROLE = "Administrator";
const GUEST_ROLE         = "Guest";
protected $allowedFields = array("id", "name", "email", "role");
public function setId($id) {
if (isset($this->fields["id"])) {
throw new BadMethodCallException(
"The ID for this user has been set already.");
}
if (!is_int($id) || $id < 1) {
throw new InvalidArgumentException(
"The user ID is invalid.");
}
$this->fields["id"] = $id;
return $this;
}
public function setName($name) {
if (strlen($name) < 2 || strlen($name) > 30) {
throw new InvalidArgumentException(
"The user name is invalid.");
}
$this->fields["name"] = htmlspecialchars(trim($name),
ENT_QUOTES);
return $this;
}
public function setEmail($email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException(
"The user email is invalid.");
}
$this->fields["email"] = $email;
return $this;
}
public function setRole($role) {
if ($role !== self::ADMINISTRATOR_ROLE &&
$role !== self::GUEST_ROLE) {
throw new InvalidArgumentException(
"The user role is invalid.");
}
$this->fields["role"] = $role;
return $this;
}
}

While the implementations of the AbstractEntity and User classes might look complex at first glance, I assure this is just a fuzzy impression. In fact, the former is a skeletal wrapper for some typical PHP magic methods, while the latter encapsulates some straightforward mutators, in order to assign the appropriate values to the fields of generic user objects.

乍看之下, AbstractEntityUser类的实现可能看起来很复杂,但我保证这只是一个模糊的印象。 实际上,前者是某些典型PHP魔术方法的骨架包装,而后者封装了一些简单的变体,以便为通用用户对象的字段分配适当的值。

With these domain classes already doing their business in relaxed insulation, let’s now do the last building block of the model. In reality, this is an optional class which can be skipped over if the situation warrants, and its responsibility is just to wrap collections of entities. Its implementation is as following:

这些领域类已经可以轻松地开展业务了,现在让我们进行模型的最后一个构建模块。 实际上,这是一个可选类,如果情况允许,可以跳过该类,它的职责只是包装实体集合。 其实现如下:

<?php
namespace ModelCollection;
use ModelEntityInterface;
interface EntityCollectionInterface extends Countable, ArrayAccess, IteratorAggregate
{
public function add(EntityInterface $entity);
public function remove(EntityInterface $entity);
public function get($key);
public function exists($key);
public function clear();
public function toArray();
}
<?php
namespace ModelCollection;
use ModelEntityInterface;
class EntityCollection implements EntityCollectionInterface
{
protected $entities = array();
public function __construct(array $entities = array())
{
if (!empty($entities)) {
$this->entities = $entities;
}
}
public function add(EntityInterface $entity) {
$this->offsetSet($entity);
}
public function remove(EntityInterface $entity) {
$this->offsetUnset($entity);
}
public function get($key) {
$this->offsetGet($key);
}
public function exists($key) {
return $this->offsetExists($key);
}
public function clear() {
$this->entities = array();
}
public function toArray() {
return $this->entities;
}
public function count() {
return count($this->entities);
}
public function offsetSet($key, $entity)
{
if (!$entity instanceof EntityInterface) {
throw new InvalidArgumentException(
"Could not add the entity to the collection.");
}
if (!isset($key)) {
$this->entities[] = $entity;
}
else {
$this->entities[$key] = $entity;
}
}
public function offsetUnset($key) {
if ($key instanceof EntityInterface) {
$this->entities = array_filter($this->entities,
function ($v) use ($key) {
return $v !== $key;
});
}
else if (isset($this->entities[$key])) {
unset($this->entities[$key]);
}
}
public function offsetGet($key) {
if (isset($this->entities[$key])) {
return $this->entities[$key];
}
}
public function offsetExists($key) {
return $key instanceof EntityInterface
? array_search($key, $this->entities)
: isset($this->entities[$key]);
}
public function getIterator() {
return new ArrayIterator($this->entities);
}
}

At this point we’ve managed to create a primitive domain model, which certainly we can use for engendering user objects without a major hassle. In doing do, we have a real chance to see if the UOW is actually the functional component it seems to be when it comes to persisting multiple entities in the database as one single transaction.

至此,我们已经成功创建了一个原始域模型,可以肯定地将其用于生成用户对象,而不会带来任何麻烦。 在这样做时,我们确实有机会了解UOW是否实际上是将一个数据库中的多个实体持久化为一个事务时的功能组件。

对UOW进行测试 (Putting the UOW Under Test)

If you’ve reached this point of the article, you probably feel like you’re being pulled in opposite directions, wondering if all of the hard up front work required in writing a bunch of interfaces and classes was really worth it. In fact, it was. Moreover, if you’re still skeptical, make sure check the following code snippet, which shows how to put the UOW to work in sweet synchrony with some naïve user objects:

如果您到了本文的这一点,您可能会觉得自己陷入了相反的方向,想知道编写一堆接口和类所需的所有艰苦的前期工作是否真的值得? 实际上是这样。 此外,如果您仍然持怀疑态度,请确保检查以下代码段,该代码段显示了如何使UOW与某些幼稚的用户对象同步工作:

<?php
require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader = new Autoloader;
$autoloader->register();
$adapter = new PdoAdapter("mysql:dbname=test", "myfancyusername",
"myhardtoguesspassword");
$unitOfWork = new UnitOfWork(new UserMapper($adapter,
new EntityCollection), new ObjectStorage);
$user1 = new User(array("name" => "John Doe",
"email" => "john@example.com"));
$unitOfWork->registerNew($user1);
$user2 = $unitOfWork->fetchById(1);
$user2->name = "Joe";
$unitOfWork->registerDirty($user2);
$user3 = $unitOfWork->fetchById(2);
$unitOfWork->registerDeleted($user3);
$user4 = $unitOfWork->fetchById(3);
$user4->name = "Julie";
$unitOfWork->commit();

Leaving aside some irrelevant details, such as assuming there’s effectively a PDO adapter living somewhere, the driving logic of the earlier script should be fairly easy to assimilate. Simply put, it shows off how to get things rolling with the UOW, which drags in some user objects from the database and queues them for insertion, update, and deletion by using the corresponding registering methods. At the end of the process, commit() just loops internally over the registered objects and performs the proper operations all in one go.

撇开一些不相关的细节,例如假设某个地方确实存在PDO适配器,早期脚本的驱动逻辑应该很容易被吸收。 简而言之,它展示了如何利用UOW滚动事物,UOW从数据库中拖出一些用户对象,并使用相应的注册方法将它们排队以进行插入,更新和删除。 在该过程的最后, commit()只是在注册对象内部循环,并一次执行所有正确的操作。

While in a standard implementation a UOW does expose the typical set of registering methods that we’d expect to see, its formal definition doesn’t provide any kind of finder. In this case, however, I decided intentionally to implement a generic one so you can see more clearly how to pull in objects from storage and in turn register them with the UOW without struggling with the oddities of a standalone, closer-to-the domain structure, such as a Repository or even an overkill Service.

尽管在标准实现中,UOW确实公开了我们期望看到的典型注册方法集,但其正式定义并未提供任何查找程序。 但是,在这种情况下,我决定有意实现一个通用对象,这样您就可以更清楚地了解如何从存储中提取对象,然后将它们注册到UOW中,而不会遇到一个独立的,离域更近的问题的困扰。结构,例如存储库或过大的服务 。

总结思想 (Closing Thoughts)

Now that you’ve peeked behind the curtain at a UOW and learned how to implement a naïve one from scratch, let your wild side show and tweak it at your will.

现在,您已经在UOW的幕后窥视了一下,并学习了如何从零开始实施幼稚的方法,让您狂野的一面展现出来并随意调整它。

Keep in mind though that while there are benefits with the pattern, it’s far from being a panacea that will solve all of the issues associated with massive accesses to the persistence layer. In enterprise-level applications that must perform expensive database writes across several places, though, a UOW provides an effective, transactional-like approach that reduces the underlying overhead, hence becoming a solid, multifaceted solution when properly coupled to a caching mechanism.

请记住,尽管此模式有好处,但它远非解决所有与大规模访问持久层相关的问题的灵丹妙药。 但是,在必须跨多个位置执行昂贵的数据库写入操作的企业级应用程序中,UOW提供了一种有效的,类似于事务的方法,可以减少基础开销,因此当正确地与缓存机制耦合时,它将成为一种可靠的,多方面的解决方案。

Image via Zhukov Oleg / Shutterstock

图片来自Zhukov Oleg / Shutterstock

翻译自: https://www.sitepoint.com/implementing-a-unit-of-work/

tp5模型能实现事务吗

tp5模型能实现事务吗_实现工作单元-通过事务模型处理域对象相关推荐

  1. 同事操作两个数据源保持事务一致_终于有人把分布式事务说清楚了

    前言 这篇文章将给大家介绍一下对分布式事务的一些见解,并讲解分布式事务处理框架 TX-LCN 的执行原理,错误之处望各位不吝指正. 1. 什么情况下需要使用分布式事务? 使用的场景很多,先举一个常见的 ...

  2. python训练模型、如何得到模型训练总时长_【绝对干货】机器学习模型训练全流程!...

    周末在家无聊闲逛github,发现一个很有趣的开源项目,作者用手绘图的方式讲解了机器学习模型构建的全流程,逻辑清晰.生动形象.同时,作者也对几张图进行了详细的讲解,学习之后,收获很多,于是将其翻译下来 ...

  3. 银行转账java代码事务实现_转账操作代码实现----事务

    在进行转账操作时,我们要保证数据的统一性.(例如,当一个账户转出金额过程中,如果出现系统错误或者说是停电断网等特殊情况,将会发生转出账户减少,转入账户金额未添加的情况.)因此,我们需要通过事务控制: ...

  4. 只读事务上下文_我可以/应该在事务上下文中使用并行流吗?

    只读事务上下文 介绍 长话短说,您不应在并行流中使用事务. 这是因为并行流中的每个线程都有其自己的名称,因此它确实参与了事务. Streams API旨在在某些准则下正常工作. 实际上,为了受益于并行 ...

  5. mysql 查询事务信息_查看MySQL最近的事务执行信息

    查看MySQL最近的事务执行信息 发布时间:2020-03-03 12:35:07 来源:51CTO 阅读:103 作者:wjw555 课题:查看MySQL最近的事务执行信息 *虽然我们可以通过查询慢 ...

  6. 模型训练平台的构建_用5行代码构建自定义训练的对象检测模型

    模型训练平台的构建 如今,机器学习和计算机视觉已成为一种热潮. 我们都已经看到了有关自动驾驶汽车和面部识别的新闻,并且可能想象到建立我们自己的计算机视觉模型将会多么酷. 但是,进入该领域并不总是那么容 ...

  7. 模型描述的关系模式_最常用的数据模型 - 关系模型

    关系模型是目前最常用地数据模型之一.关系型数据库系统采用关系模型作为数据的组织方式,在关系模型中用表格结构表达实体集,以及实体集之间的联系,其最大特点是描述的一致性.关系模型是由若干个关系模式组成的集 ...

  8. OSI的七层模型,网线,网卡,集线器,交换机,路由器分别工作在七层模型中的哪一层?

    OSI七层网络模型由下至上为1至7层,分别为物理层(Physical layer),数据链路层(Data link layer),网络层(Network layer),传输层(Transport la ...

  9. mysql事务并发控制_数据库 事务并发控制

    事务是一个逻辑工作单元, SQLServer 2005 提供了几种自动的可以通过编程来完成的机制,包括事务日志. SQL 事务控制语句,以及事务处理运行过程中通过锁定保证数据完整性的机制.当用户对数据 ...

最新文章

  1. C#中调用Windows API的要点
  2. python def函数报错详解_【python】详解python函数定义 def()与参数args、可变参数*args、关键参数**args使用实例...
  3. python3.7安装pip问题_python3.7安装, 解决pip is configured with locations that require TLS/SSL问题...
  4. 聚簇索引和聚簇索引介绍
  5. SKLEARN模型选择
  6. oracle9i怎样管理数据,Oracle9i数据库管理员使用大全
  7. 机器学习算法基础7-计算范围内素数的五种算法
  8. 如何使用 python 爬取全国小区名称
  9. 金山词霸java接口_使用金山词霸API做翻译(c语言实现)
  10. stm32 uv5打开uv4工程错误
  11. revit 二次开发之创建图纸和放置视图
  12. 相机响应曲线、ISO详解
  13. 数字媒体概论——声音
  14. Deepin20.6直接运行exe文件
  15. 快来看,这些心理学家与诺贝尔奖有关系
  16. WPF入门8:模板(Template)
  17. vmware设置内外网双网卡(均是独立IP)
  18. 实景三维在自然资源地质环境监测预警的应用
  19. git pull报没有足够内存 not enough memory for initialization
  20. OSPF区域MD5认证

热门文章

  1. 亚马逊,速卖通,阿里国际,shopee等卖家如何补单?
  2. python网络编程实战_Python 异步网络编程实战
  3. 深度解析Go中的用户输入获取(fmt.Scan fmt.Scanln fmt.Scanf),含多项测试及源码解读
  4. 一文看懂CDN加速原理
  5. 【SuperMap .Net 组件】三维自定义专题图
  6. 外星人电脑运行linux,Alienware 又推出了两台轻薄游戏笔记本
  7. 【Linux】文件查找locate
  8. SLI导致双显卡被TensorFlow同时占用问题(Windows下)
  9. 斗地主拿到双王的概率问题
  10. python封面 老鼠_pygame老鼠过街-配音与封面版本