在正式展开之前,有一些概念要先做一个界定。首先: 领域 模型是指系统应对的 领域 中所有逻辑的一个抽象,本质上它是 领域 中各种对象和概念以及它们之间关系的 集合 。你可以用自然语言描述它,也可以用UML来描述,或者是代码去描述。特别地,当我们使用面
在正式展开之前,有一些概念要先做一个界定。首先:领域模型是指系统应对的领域中所有逻辑的一个抽象,本质上它是领域中各种对象和概念以及它们之间关系的集合。你可以用自然语言描述它,也可以用UML来描述,或者是代码去描述。特别地,当我们使用面向对象建模技术来实现这个领域模型时,我们可以把这个实现出来的模型称之为对象模型。我们可以认为领域模型是一个概念模型,是分析阶段的产物。让精心构建的对象模型高效地工作有很多底层的技术问题需要解决,其中如何满足领域对象的业务方法在计算过程中对数据的需求是一个普遍存在的问题(实际上,在实际应用中,我们会遇到更为复杂的情况,不只是有数据的需求,还可能出现对应用层面发生依赖)。对于这一问题,目前有两种模型可供借鉴,那就是基于数据访问的集合类和领域事件模式。
基于数据访问的集合类(Data Access Based Collection)
基于数据访问的集合类是我在开发oobbs系统时设计的一种模式。这一模式通过一个抽象的接口来代表某一对象依赖的一组集合。当这一对象实例化时,一个基于数据访问的集合实现类会注入到这个对象中,所有通过这一集合进行的操作,比如遍历,增删元素等都是被实现类转化成数据访问操作。基于数据访问的集合类很像是一个缩水版的Repository。还是以Forum的public List
package oobbs.domainmodel; import java.io.Serializable; import java.util.List; /** * The collection interface represents a set of objects, it's like the * java.util.Collection, however, there no real objects in this collection, it * only looks like a collection, its method's implementation is database access * operation! see <code>oobbs.infrastructure.persistence.AbstractHibernateCollection</code> * @author laurence.geng */ public interface Collection<entity pk extends serializable owner> { void setOwner(Owner owner); void setOwnerName(String ownerName); /** * Adds an object. This method will persist entity to database directly! * @param e an entity instance. * @return the pK the generated primary key * after insert into database. */ PK add(Entity e); void addAll(java.util.Collection<entity> c); /** * Removes the entity. This method will remove this entity from database * directly. */ void remove(Entity e); void removeAll(java.util.Collection<entity> c); boolean contains(Entity o); boolean isEmpty(); int size(); /** * The most important method. It returns a subset of the whole collection. * the returned subset is fetched from database by sql, hql or other data * access way, The Collection itself never load all elements once time! */ List<entity> toList(int startIndex, int offset); void flush(); }</entity></entity></entity></entity>
下面则是基本于hibernate的集合接口实现类。它实现了所有的基本的操作。在Forum类中就会这样一个字段以及相应的getter和setter:
@Transient private Collection<thread long forum> forumThreads; @Autowired /** * Sets ForumThreadCollection. * ForumThreadCollection is injected by this setter. When a collection instance injected, set this forum to its forum! */ public void setForumThreads(@Qualifier("forumThreads") Collection<thread long forum> forumThreads) { this.forumThreads = forumThreads; this.forumThreads.setOwner(this); this.forumThreads.setOwnerName("forum"); } /** * Gets this forum's thread collection. */ public Collection<thread long forum> getForumThreads() { return forumThreads; }</thread></thread></thread>
其中注入的forumThreads对象是一个名为ForumThreadHibernateCollection的类,它继承了AbstractHibernateCollection类,因为没有特殊的需要,没有重写任何方法。而下面展示的是service中对这集合的一次使用:
List<thread> threads = forum.getForumThreads().toList(startThreadIndex,threadTotal);</thread>
我们来分析一下Domain Collection这一模式的优劣。我认为它最大的优点在于它能够以一个字段的形式存在于单端关联对象中,这使得单端对象的定义饱满,完成符合并体现了一对多双向关联中双方依赖关系。这一点是使用hiberate映射无法实现的,因为我们不能在Forum中映射@OneToMany(mappedBy="forum") private Set
但是它的缺点也是非常明显并且似乎是无法克服的,那就是它只能用来表示直接关联的集合,如果单端对象想通过这一集合进一步遍历元素中更深层次的二级,三级集合时,domain collection就显得力不从心了。比方说:在论坛的首页上往往会罗列出各个Forum的一些基本信息,其中之一就是该Forum有多少帖子(Post),相应的,Forum对象会有这样一个方法public Long getPostCount();很显然,Post是Forum的二级集合,一个Forum需要先得到它的Thread集合,再从每个Thread中得到Post集合。我们可以为了这一方法再提供一个ForumPostHibernateCollcetion用来代表一个Forum的所有Post的集合,但是这个集合已经和模型的定义发生了偏离,因为并没有从Forum到Post的直接关联。而更加普遍的问题的是:我们会常常遇到某一个单端实体从它直接依赖的对象开始进行深度地导航(体现在SQL上就是对多个表的join操作),这时候我们不能为每一种导航而创建一个从出发点到结束点的domain collection。在这种情况下,获取数据必须通过另外一种方式进行了,那就是Domain Event模式。
领域事件(Domain Event)
Domain Event模式最初由udi dahan提出,发表在自己的博客上:http://www.udidahan.com/2009/06/14/domain-events-salvation/这一模式得到广泛的认可。它所要应对的正是将领域对象从对repository或service的依赖中解脱出来,避免让领域对象对这些设施产生直接依赖。它的做法就是当领域对象的业务方法需要依赖到这些对象时就发出一个事件,这个事件会被相应的对象监听到并做出处理。在我的oobbs系统中,我对这一模式做了一些改进,主要是消除静态方法和规范事件模型。在我的方案中,这一机制由这样几个角色:DomainEventDispatcher,DomainEvent和DomainEventListener.
DomainEventDispatcher会以字段的形式存在于领域对象中,负责在业务方法中dispatch领域事件。下面是所有Dispatcher的基类:
package oobbs.domainmodel; import java.util.HashMap; import java.util.Map; /** * The DomainEventDispatcher take charge of listener registration and dispatch * domain event to corresponding listener to handle. Usually, one dispatch per * domain object. */ public class DomainEventDispatcher { /** The listener map. all registered listeners are stored in this map. */ protected Map<string domainobejctlistener> listeners = new HashMap<string domainobejctlistener>(); /** * Adds a listener. * * @param listener the listener */ public void addListener(DomainObejctListener listener) { listeners.put(listener.getName(), listener); } /** * Removes all listeners. */ public void removeAllListeners() { listeners.clear(); } }</string></string>
一般来说一个领域对象会有一个对应的event dispatcher,这个dispatcher会有一组重载的dispatch方法,用于分发不同的领域事件。下面是oobbs中Forum对象的event dispatcher.
package oobbs.domainmodel.forum; import oobbs.Constants; import oobbs.domainmodel.DomainEventDispatcher; import oobbs.domainmodel.ResultCollector; /** * * The ForumEventDispatcher dispatch all events which about Forum object. * @author * laurence.geng */ public class ForumEventDispatcher extends DomainEventDispatcher { public void dispatch(GetForumThreadEvent event, ResultCollector result) { ForumListener forumListener = (ForumListener) listeners.get(Constants.FORUM_REPO_AS_FORUM_LISTENER); forumListener.handleGetForumThreadEvent(event, result); } public void dispatch(GetForumPostCountEvent event, ResultCollector result) { ForumListener forumListener = (ForumListener) listeners.get(Constants.FORUM_REPO_AS_FORUM_LISTENER); forumListener.handleGetFroumPostCountEvent(event, result); } public void dispatch(GetForumThreadCountEvent event, ResultCollector result) { ForumListener forumListener = (ForumListener) listeners.get(Constants.FORUM_REPO_AS_FORUM_LISTENER); forumListener.handleGetForumThreadCountEvent(event, result); } }
系统中会有很多的domain event,下面是所有领域事件的基类:
package oobbs.domainmodel; /** * The supper class of all domain events. all events should provide event * source, the domain object which fired this event. */ public class DomainEvent { /** The event source, the domain object which fired this event. */ protected Object source; public DomainEvent(Object source) { super(); this.source = source; } /** * Gets the event source. * * @return the event source */ public Object getSource() { return source; } }
下面就是刚才提到的例子中返回Forum某一部分(分页)Thread的事件,在这个事件中我们看到一个事件可以携带一些参数,供listener使用:
package oobbs.domainmodel.forum; import oobbs.domainmodel.DomainEvent; /** * The Event that forum request to get its threads. * @author laurence.geng */ public class GetForumThreadEvent extends DomainEvent { /** The start index of request thread. */ private int startIndex; /** The count of request thread. */ private int count; public GetForumThreadEvent(Forum source, int startIndex, int count) { super(source); this.startIndex = startIndex; this.count = count; } public int getStartIndex() { return startIndex; } public int getCount() { return count; } }
而下面就是我们所有listener的基类:
package oobbs.domainmodel; /** * The super class of all domain object listeners. it handles events from * domain objects. Each listener has to provide a name as key for registering * itself to dispatcher. * @see DomainObejctEvent * @author laurence.geng */ public interface DomainObejctListener { /** * Gets the name. * * @return the name */ public String getName(); }
下面是Forum的listenerr接口,这个接口会有很多handle方法,代码只展示了一个。
package oobbs.domainmodel.forum; import oobbs.domainmodel.DomainObejctListener; import oobbs.domainmodel.ResultCollector; /** * * The forum listener. It handles all events from Forum object. * * @see * ForumEvent * @author laurence.geng */ public interface ForumListener extends DomainObejctListener { /** * * Handle the event that a forum requests to get its threads. * * @param * event the event * @param result the result */ public void handleGetForumThreadEvent(GetForumThreadEvent event,ResultCollector result); }
然后 是这个接口一个实现类,在oobbs中,做为forum的repository的实现类:ForumHibernateRepository,自然成为实现这一接口的最佳选择:
/** * * The Forum's repository with hibernate implementation, besides, it's a forum * listener which handle * all events come from forum object. */ public class ForumHibernateRepository extends AbstractHibernateRepository<forum long> implements ForumRepository { /* * (non-Javadoc) * * @see * oobbs.domainmodel.forum.ForumListener#handleGetForumThreadEvent(oobbs * .domainmodel.forum.GetForumThreadEvent, * oobbs.domainmodel.ResultCollector) */ @Overridepublic void handleGetForumThreadEvent(final GetForumThreadEvent event, ResultCollector result) { List<thread> threads = (List<thread>) getHibernateTemplate().executeWithNativeSession(new HibernateCallback() { @SuppressWarnings("unchecked") public Object doInHibernate(Session session) throws HibernateException, SQLException { logger.info("Start to load threads of forum."); // Get forum. String getForuumThreadHql = "from Thread as thread where thread.forum=:forum"; List<thread> threads = (List<thread>) session .createQuery(getForuumThreadHql) .setCacheable(true) .setParameter("forum", event.getSource()) .setFirstResult(event.getStartIndex()) .setMaxResults(event.getCount()).list(); return threads; } }); result.add(threads); } } </thread></thread></thread></thread></forum>
最后我们看一看这一切是如被触发的。我们来看forum的这个方法:oobbs.domainmodel.forum.Forum.getThreads(int, int):
public List<thread> getThreads(int startIndex, int count) { GetForumThreadEvent event = new GetForumThreadEvent(this, startIndex, count); ResultCollector result = new ResultCollector(); forumEventDispatcher.dispatch(event, result); return (List<thread>) result.getUniqueResult(); }</thread></thread>
很简洁的四行代码:分别new一个event和result collector,然后用ForumEventDispatcher来dispatch这个事件,然后收集返回的处理结果。上述所有组件都是围绕这里而设计的,我们希望在领域对象的业务方法里不会出现任何repository或service,DomainEvent模式很好的满足了我们的需求,并且是以一种非常优雅而简洁的方式。
当然,在上面讲述的整个机制中,我们漏掉了一环没有讲,那就是dispatcher是如何被实例化并完成注册listener工作的。由于oobbs使用了spring的IOC管理对象, dispatcher是由IOC创建并完成注册listener等初始化工作的。下面的类完成了这一系列工作:
package oobbs.infrastructure.appcontext; import oobbs.domainmodel.DomainObejctListener; import oobbs.domainmodel.forum.ForumEventDispatcher; import oobbs.domainmodel.forum.ThreadEventDispatcher; import org.apache.log4j.Logger; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * * The ApplicationBeanPostProcessor will do some initialization work when bean * is created by spring ioc container, * such as: Adding listeners for domain * event dispatchers and so on. * @author laurence.geng */ public class ApplicationBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware { /** The Constant logger. */ private static final Logger logger = Logger .getLogger(ApplicationBeanPostProcessor.class); /** The application context. */ private ApplicationContext applicationContext; /* * (non-Javadoc) * @see org.springframework .beans.factory * .config.BeanPostProcessor #postProcessBeforeInitialization * (java.lang.Object, java.lang.String) */ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; // we could potentially return any object reference here... } /* * (non-Javadoc) * @see * org.springframework.beans.factory.config.BeanPostProcessor * #postProcessAfterInitialization(java.lang.Object, java.lang.String) */ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { logger.debug("Bean '" + beanName + "' created : " + bean.toString()); // Adding listeners for domain event dispatchers. if ("forumEventDispatcher".equals(beanName)) { ForumEventDispatcher forumEventDispatcher = (ForumEventDispatcher) bean; forumEventDispatcher.addListener((DomainObejctListener) applicationContext.getBean("forumRepository")); } if ("threadEventDispatcher".equals(beanName)) { ThreadEventDispatcher threadEventDispatcher = (ThreadEventDispatcher) bean; threadEventDispatcher.addListener((DomainObejctListener) applicationContext.getBean("threadRepository")); } return bean; } /* * (non-Javadoc) * @see * org.springframework.context.ApplicationContextAware#setApplicationContext * (org.springframework.context.ApplicationContext) */ @Override public void setApplicationContext(ApplicationContext arg0) throws BeansException { this.applicationContext = arg0; } }
小结

MySQLスロークエリを最適化するには、slowquerylogとperformance_schemaを使用する必要があります。1。LowerQueryLogを有効にし、しきい値を設定して、スロークエリを記録します。 2。performance_schemaを使用してクエリの実行の詳細を分析し、パフォーマンスのボトルネックを見つけて最適化します。

MySQLとSQLは、開発者にとって不可欠なスキルです。 1.MYSQLはオープンソースのリレーショナルデータベース管理システムであり、SQLはデータベースの管理と操作に使用される標準言語です。 2.MYSQLは、効率的なデータストレージと検索機能を介して複数のストレージエンジンをサポートし、SQLは簡単なステートメントを通じて複雑なデータ操作を完了します。 3.使用の例には、条件によるフィルタリングやソートなどの基本的なクエリと高度なクエリが含まれます。 4.一般的なエラーには、SQLステートメントをチェックして説明コマンドを使用することで最適化できる構文エラーとパフォーマンスの問題が含まれます。 5.パフォーマンス最適化手法には、インデックスの使用、フルテーブルスキャンの回避、参加操作の最適化、コードの読み取り可能性の向上が含まれます。

MySQL非同期マスタースレーブレプリケーションにより、BINLOGを介したデータの同期が可能になり、読み取りパフォーマンスと高可用性が向上します。 1)マスターサーバーレコードはBinlogに変更されます。 2)スレーブサーバーは、I/Oスレッドを介してBINLOGを読み取ります。 3)サーバーSQLスレッドは、BINLOGを適用してデータを同期させます。

MySQLは、オープンソースのリレーショナルデータベース管理システムです。 1)データベースとテーブルの作成:createdatabaseおよびcreateTableコマンドを使用します。 2)基本操作:挿入、更新、削除、選択。 3)高度な操作:参加、サブクエリ、トランザクション処理。 4)デバッグスキル:構文、データ型、およびアクセス許可を確認します。 5)最適化の提案:インデックスを使用し、選択*を避け、トランザクションを使用します。

MySQLのインストールと基本操作には、次のものが含まれます。1。mysqlをダウンロードしてインストールし、ルートユーザーパスワードを設定します。 2。sqlコマンドを使用して、createdatabaseやcreateTableなどのデータベースとテーブルを作成します。 3. CRUD操作を実行し、挿入、選択、更新、コマンドを削除します。 4.パフォーマンスを最適化し、複雑なロジックを実装するためのインデックスとストアドプロシージャを作成します。これらの手順を使用すると、MySQLデータベースをゼロから構築および管理できます。

Innodbbufferpoolは、データとインデックスページをメモリにロードすることにより、MySQLデータベースのパフォーマンスを向上させます。 1)データページは、ディスクI/Oを削減するためにBufferPoolにロードされます。 2)汚れたページは、定期的にディスクにマークされ、リフレッシュされます。 3)LRUアルゴリズム管理データページの排除。 4)読み出しメカニズムは、可能なデータページを事前にロードします。

MySQLは、インストールが簡単で、強力で管理しやすいため、初心者に適しています。 1.さまざまなオペレーティングシステムに適した、単純なインストールと構成。 2。データベースとテーブルの作成、挿入、クエリ、更新、削除などの基本操作をサポートします。 3.参加オペレーションやサブクエリなどの高度な機能を提供します。 4.インデックス、クエリの最適化、テーブルパーティション化により、パフォーマンスを改善できます。 5。データのセキュリティと一貫性を確保するために、バックアップ、リカバリ、セキュリティ対策をサポートします。

完全なテーブルスキャンは、MySQLでインデックスを使用するよりも速い場合があります。特定のケースには以下が含まれます。1)データボリュームは小さい。 2)クエリが大量のデータを返すとき。 3)インデックス列が高度に選択的でない場合。 4)複雑なクエリの場合。クエリプランを分析し、インデックスを最適化し、オーバーインデックスを回避し、テーブルを定期的にメンテナンスすることにより、実際のアプリケーションで最良の選択をすることができます。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

mPDF
mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

SublimeText3 Linux 新バージョン
SublimeText3 Linux 最新バージョン

MantisBT
Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。

SublimeText3 中国語版
中国語版、とても使いやすい

Safe Exam Browser
Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

ホットトピック



