springboot,jpa 添加全文索引

时间:2019-04-04 13:00:18   收藏:0   阅读:174

因项目中需要对表的一些列进行全文索引,但是jpa是不带全文索引创建的功能,所以自己写了一个创建全文索引注解

来自动创建全文索引,需要配合spring 或 springboot使用,一定要spring扫描到如下类:

ApplicationUtil.java
ClassScaner.java
FulltextDelegator.java

 

1.获取spring环境的上下文

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;

/**
 * 获取上下文
 * @project order
 * @fileName ApplicationUtil.java
 * @Description
 * @author light-zhang
 * @date 2019年4月3日
 * @version 1.0.0
 */
@Configuration
public class ApplicationUtil implements ApplicationContextAware { 
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationUtil.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
}

2.扫描指定包下面的所有类信息

import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.StringUtils;
import org.springframework.util.SystemPropertyUtils;

/**
 * 扫描指定的包下面的类信息
 * 
 * @project order
 * @fileName ClassScaner.java
 * @Description
 * @author light-zhang
 * @date 2019年4月2日
 * @version 1.0.0
 */
@Configuration
public class ClassScaner implements ResourceLoaderAware {
    
    private static final String FULLTEXT_SACN_PACKAGE_PATH = "fulltext.scan.package";

    private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>();
    private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();
    private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(
            this.resourcePatternResolver);

    public static Set<Class<?>> scan() {
        String scanPath = ApplicationUtil.getApplicationContext().getEnvironment().getProperty(FULLTEXT_SACN_PACKAGE_PATH);
        if (!StringUtils.isEmpty(scanPath)) {
            return ClassScaner.scan(StringUtils.tokenizeToStringArray(scanPath, ",; \t\n"));
        }
        return null;
    }

    public static Set<Class<?>> scan(String[] basePackages) {
        ClassScaner cs = new ClassScaner();
        Set<Class<?>> classes = new HashSet<Class<?>>();
        for (String s : basePackages) {
            classes.addAll(cs.doScan(s));
        }
        return classes;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
    }

    public Set<Class<?>> doScan(String basePackage) {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        try {
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                    .concat(org.springframework.util.ClassUtils
                            .convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage))
                            .concat("/**/*.class"));
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            MetadataReader metadataReader = null;
            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    if ((includeFilters.size() == 0 && excludeFilters.size() == 0) || matches(metadataReader)) {
                        try {
                            classes.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        } catch (IOException ex) {
            throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
        }
        return classes;
    }

    protected boolean matches(MetadataReader metadataReader) throws IOException {
        for (TypeFilter tf : this.excludeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return false;
            }
        }
        for (TypeFilter tf : this.includeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return true;
            }
        }
        return false;
    }
}

3.创建全文索引注解

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 全文索引注解
 * 
 * @作者 light-zhang
 * @时间 2019年3月13日
 * @product order
 * @package cc.zeelan.mall.order.common
 * @file FruitProvider.java
 *
 */
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface Fulltext {
    /**
     * 索引名称
     * 
     * @return
     */
    String IndexesName() default "";

    /**
     * 对应的列
     */
    String columnName(); 
}

4.注解委托实现 

import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.Table;

import org.hibernate.AnnotationException;
import org.hibernate.Session;
import org.hibernate.query.NativeQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

/**
 * 全文索引注解实现
 * 
 * @作者 light-zhang
 * @时间 2019年3月13日
 * @product order
 * @package cc.zeelan.mall.order.common.util
 * @file FulltextKeyImpl.java
 * @DLL
 */
@Configuration
@Transactional
public class FulltextDelegator implements CommandLineRunner {
    @Autowired
    private EntityManager entityManager;

    private Session session;

    @Override
    public void run(String... args) throws Exception {
        Set<Class<?>> clazzs = ClassScaner.scan();
        if (!Objects.isNull(clazzs)) {
            for (Class<?> clazz : clazzs) {
                getAnnotation(clazz);
            }
        }
    }

    /**
     * 处理全文注解相关
     * 
     * @param clazz
     */
    public void getAnnotation(Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        String indexName = null;
        for (Field field : fields) {
            if (field.isAnnotationPresent(Fulltext.class)) {
                Table table = clazz.getAnnotation(Table.class);
                if (StringUtils.isEmpty(table.name())) {
                    throw new AnnotationException("@table(name=‘‘) specified for entity: ".concat(clazz.getName()));
                }
                Fulltext fulltextKey = field.getAnnotation(Fulltext.class);
                if (!StringUtils.isEmpty(fulltextKey.IndexesName())) {
                    indexName = fulltextKey.IndexesName();
                } else {
                    indexName = table.name().concat("_").concat(fulltextKey.columnName()).concat("_idx");
                }
                if (isExist(table.name(), indexName) > 0) {// 当存在索引就删除当前的旧索引
                    deleteIndex(table.name(), indexName);
                }
                createIndex(table.name(), indexName, fulltextKey.columnName());
            }
        }
        entityManager.flush();
        entityManager.close();
    }

    /**
     * 创建全文索引
     */
    private void createIndex(String tableName, String indexName, String columnName) {
        final StringBuffer createIndex = new StringBuffer("CREATE FULLTEXT INDEX ");// 執行創建索引
        createIndex.append(indexName);
        createIndex.append(" ON ");
        createIndex.append(tableName);
        createIndex.append("(");
        createIndex.append(columnName);
        createIndex.append(")");
        excute(createIndex.toString());// 在执行创建全文索引操作
        createIndex.setLength(0);// 清空
    }

    /**
     * 删除索引
     */
    private void deleteIndex(String tableName, String indexName) {
        final StringBuffer deleteIndex = new StringBuffer("ALTER TABLE ");// 執行刪除索引
        deleteIndex.append(tableName);
        deleteIndex.append(" DROP INDEX ");
        deleteIndex.append(indexName);
        excute(deleteIndex.toString());// 先执行删除索引操作
        deleteIndex.setLength(0);// 清空
    }

    /**
     * 索引是否存在
     * 
     * @return
     */
    private int isExist(String tableName, String indexName) {
        final StringBuffer existIndex = new StringBuffer("SHOW INDEX FROM ");// 執行刪除索引
        existIndex.append(tableName);
        existIndex.append(" WHERE key_name LIKE ‘");
        existIndex.append(indexName.concat("%‘"));
        int result = excuteQuery(existIndex.toString());
        existIndex.setLength(0);
        return result;
    }

    /**
     * 执行创建全文索引SQL
     * 
     * @param sql
     * @return
     */
    @Modifying(clearAutomatically = true)
    public int excute(String sql) {
        Query query = entityManager.createNativeQuery(sql);
        return query.executeUpdate();
    }

    /**
     * 查询全文索引工具
     * 
     * @param sql
     * @return
     */
    public int excuteQuery(String sql) {
        session = entityManager.unwrap(org.hibernate.Session.class);
        NativeQuery<?> query = session.createSQLQuery(sql);
        List<?> arrays = query.getResultList();
        if (!CollectionUtils.isEmpty(arrays)) {
            return arrays.size();
        }
        return 0;
    } 
}

5.使用方法

 1.先在application.properties下面配置:fulltext.scan.package=xxx.xxx.entity 要扫描类的包路径 

 IndexesName:表示要生成的全文索引名称,如果不赋值的情况下默认的生成策略为:表名_列名_idx  || columnName:对应的列名


   @Column(name = "order_ids", columnDefinition = "text COLLATE utf8_unicode_ci COMMENT ‘订单ids‘ ")
    @Fulltext(columnName = "order_ids")
    private String orderIds;

 

原文:https://www.cnblogs.com/light-zhang/p/10653991.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!