上一节咱们分析了广告索引的维护有2种,全量索引加载
和增量索引维护
。由于广告检索是广告系统中最为重要的环节,你们必定要认真理解咱们索引设计的思路,接下来咱们来编码实现索引维护功能。html
咱们来定义一个接口,来接收全部index的增删改查操做,接口定义一个范型,来接收2个参数,K
表明咱们索引的健值,V
表明返回值。java
/** * IIndexAware for 实现广告索引的增删改查 * * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a> */
public interface IIndexAware<K, V> {
/** * 经过key 获取索引 */
V get(K key);
/** * 添加索引 * @param key * @param value */
void add(K key, V value);
/** * 更新索引 */
void update(K key, V value);
/** * 删除索引 */
void delete(K key, V value);
}
复制代码
咱们必定要知道,并非全部的数据库表都须要建立索引,好比User
表咱们在数据检索的时候实际上是不须要的,固然也就不必建立索引,而且,也不是表中的全部字段都须要索引,这个也是根据具体的业务来肯定字段信息,好比咱们接下来要编写的推广计划
索引中,推广计划名称就能够不须要。下面,咱们来实现咱们的第一个正向索引
。git
/** * AdPlanIndexObject for 推广计划索引对象 * 这个索引对象咱们没有添加 推广计划名称 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a> */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdPlanIndexObject {
private Long planId;
private Long userId;
private Integer planStatus;
private Date startDate;
private Date endDate;
/** * 根据实际字段来更新索引 */
public void update(AdPlanIndexObject newObject) {
if (null != newObject.getPlanId()) {
this.planId = newObject.getPlanId();
}
if (null != newObject.getUserId()) {
this.userId = newObject.getUserId();
}
if (null != newObject.getPlanStatus()) {
this.planStatus = newObject.getPlanStatus();
}
if (null != newObject.getStartDate()) {
this.startDate = newObject.getStartDate();
}
if (null != newObject.getEndDate()) {
this.endDate = newObject.getEndDate();
}
}
}
复制代码
IIndexAware
接口。/** * AdPlanIndexAwareImpl for 推广计划索引实现类 * * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a> */
@Slf4j
@Component
public class AdPlanIndexAwareImpl implements IIndexAware<Long, AdPlanIndexObject> {
private static Map<Long, AdPlanIndexObject> planIndexObjectMap;
/** * 由于操做索引的过程当中有可能对索引进行更新,为了防止多线程形成的线程不安全问题,咱们不能使用hashmap,须要实现ConcurrentHashMap */
static {
planIndexObjectMap = new ConcurrentHashMap<>();
}
@Override
public AdPlanIndexObject get(Long key) {
return planIndexObjectMap.get(key);
}
@Override
public void add(Long key, AdPlanIndexObject value) {
log.info("AdPlanIndexAwareImpl before add::{}", planIndexObjectMap);
planIndexObjectMap.put(key, value);
log.info("AdPlanIndexAwareImpl after add::{}", planIndexObjectMap);
}
@Override
public void update(Long key, AdPlanIndexObject value) {
log.info("AdPlanIndexAwareImpl before update::{}", planIndexObjectMap);
//查询当前的索引信息,若是不存在,直接新增索引信息
AdPlanIndexObject oldObj = planIndexObjectMap.get(key);
if (null == oldObj) {
planIndexObjectMap.put(key, value);
} else {
oldObj.update(value);
}
log.info("AdPlanIndexAwareImpl after update::{}", planIndexObjectMap);
}
@Override
public void delete(Long key, AdPlanIndexObject value) {
log.info("AdPlanIndexAwareImpl before delete::{}", planIndexObjectMap);
planIndexObjectMap.remove(key);
log.info("AdPlanIndexAwareImpl after delete::{}", planIndexObjectMap);
}
}
复制代码
至此,咱们已经完成了推广计划的索引对象和索引操做的代码编写,你们能够参考上面的示例,依次完成推广单元
、推广创意
、地域
、兴趣
、关键词
以及推广创意和推广单元的关联索引
,或者可直接从 Github传送门 / Gitee传送门 下载源码。程序员
按照上述代码展现,咱们已经实现了全部的索引操做的定义,可是实际状况中,咱们须要使用这些服务的时候,须要在每个Service中
@Autowired
注入,咱们那么多的索引操做类,还不包含后续还有可能须要新增的索引维度,工做量实在是太大,并且不方便维护,做为一个合格的程序员来讲,这是很是不友好的,也许会让后续的开发人员骂娘。github
为了防止后续被骂,咱们来编写一个索引缓存工具类com.sxzhongf.ad.index.IndexDataTableUtils
,经过这个索引缓存工具类来实现一次注入,解决后顾之忧。要实现这个工具类,咱们须要实现2个接口:org.springframework.context.ApplicationContextAware
和org.springframework.core.PriorityOrdered
spring
org.springframework.context.ApplicationContextAware
, 统一经过实现该接口的类,来操做Spring容器以及其中的Bean实例。 在Spring中,以Aware
为后缀结束的类,你们能够简单的理解为应用程序想要XXX,好比ApplicationContextAware
表明应用程序想要ApplicationContext
,BeanFactoryAware
表示应用程序想要BeanFactory
...等等org.springframework.core.PriorityOrdered
组件加载顺序,也能够使用org.springframework.core.Ordered
如下代码为咱们的工具类:package com.sxzhongf.ad.index;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.PriorityOrdered;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** * IndexDataTableUtils for 全部索引服务须要缓存的Java Bean * * 使用方式: * 获取{@link com.sxzhongf.ad.index.creative.CreativeIndexAwareImpl}索引服务类 * 以下: * {@code * IndexDataTableUtils.of(CreativeIndexAwareImpl.class) * } * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a> */
@Component
public class IndexDataTableUtils implements ApplicationContextAware, PriorityOrdered {
//注入ApplicationContext
private static ApplicationContext applicationContext;
/** * 定义用于保存全部Index的Map * Class标示咱们的索引类 */
private static final Map<Class, Object> dataTableMap = new ConcurrentHashMap<>();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
IndexDataTableUtils.applicationContext = applicationContext;
}
/** * 获取索引服务缓存 */
public static <T> T of(Class<T> klass) {
T instance = (T) dataTableMap.get(klass);
//若是获取到索引bean,直接返回当前bean
if (null != instance) {
return instance;
}
//首次获取索引bean为空,写入Map
dataTableMap.put(klass, bean(klass));
return (T) dataTableMap.get(klass);
}
/** * 获取Spring 容器中的Bean对象 */
private static <T> T bean(String beanName) {
return (T) applicationContext.getBean(beanName);
}
/** * 获取Spring 容器中的Bean对象 */
private static <T> T bean(Class klass) {
return (T) applicationContext.getBean(klass);
}
@Override
public int getOrder() {
return PriorityOrdered.HIGHEST_PRECEDENCE;
}
}
复制代码