论基于数据访问的集合类(Data Access Based Collection)和领域
在正式展开之前,有一些概念要先做一个界定。首先: 领域 模型是指系统应对的 领域 中所有逻辑的一个抽象,本质上它是 领域 中各种对象和概念以及它们之间关系的 集合 。你可以用自然语言描述它,也可以用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; } }
小结

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics











SQL IF statements are used to conditionally execute SQL statements, with the syntax as: IF (condition) THEN {statement} ELSE {statement} END IF;. The condition can be any valid SQL expression, and if the condition is true, execute the THEN clause; if the condition is false, execute the ELSE clause. IF statements can be nested, allowing for more complex conditional checks.

How to configure Zend in Apache? The steps to configure Zend Framework in an Apache Web Server are as follows: Install Zend Framework and extract it into the Web Server directory. Create a .htaccess file. Create the Zend application directory and add the index.php file. Configure the Zend application (application.ini). Restart the Apache Web server.

The main reasons why you cannot log in to MySQL as root are permission problems, configuration file errors, password inconsistent, socket file problems, or firewall interception. The solution includes: check whether the bind-address parameter in the configuration file is configured correctly. Check whether the root user permissions have been modified or deleted and reset. Verify that the password is accurate, including case and special characters. Check socket file permission settings and paths. Check that the firewall blocks connections to the MySQL server.

This article describes how to effectively monitor the SSL performance of Nginx servers on Debian systems. We will use NginxExporter to export Nginx status data to Prometheus and then visually display it through Grafana. Step 1: Configuring Nginx First, we need to enable the stub_status module in the Nginx configuration file to obtain the status information of Nginx. Add the following snippet in your Nginx configuration file (usually located in /etc/nginx/nginx.conf or its include file): location/nginx_status{stub_status

The key to PHPMyAdmin security defense strategy is: 1. Use the latest version of PHPMyAdmin and regularly update PHP and MySQL; 2. Strictly control access rights, use .htaccess or web server access control; 3. Enable strong password and two-factor authentication; 4. Back up the database regularly; 5. Carefully check the configuration files to avoid exposing sensitive information; 6. Use Web Application Firewall (WAF); 7. Carry out security audits. These measures can effectively reduce the security risks caused by PHPMyAdmin due to improper configuration, over-old version or environmental security risks, and ensure the security of the database.

VprocesserazrabotkiveB-enclosed, Мнепришлостольностьсясзадачейтерациигооглапидляпапакробоглесхетсigootrive. LEAVALLYSUMBALLANCEFRIABLANCEFAUMDOPTOMATIFICATION, ČtookazaLovnetakProsto, Kakaožidal.Posenesko

Effective monitoring and defense against malicious website access is crucial to the Apache server on the Debian system. Apache access logs are the key source of information to identify such threats. This article will guide you on how to analyze logs and take defensive measures. The Apache access log that identifies malicious access behaviors Debian systems is usually located in /var/log/apache2/access.log. You can analyze the logs in a variety of ways: Log file location confirmation: First, please confirm the exact location of your Apache access log, which may vary slightly depending on the system configuration. Command line tool analysis: Use grep command to search for specific patterns, such as grep "404"

Apache server is a powerful web server software that acts as a bridge between browsers and website servers. 1. It handles HTTP requests and returns web page content based on requests; 2. Modular design allows extended functions, such as support for SSL encryption and dynamic web pages; 3. Configuration files (such as virtual host configurations) need to be carefully set to avoid security vulnerabilities, and optimize performance parameters, such as thread count and timeout time, in order to build high-performance and secure web applications.
