springboot,jpa 添加全文索引


因項目中需要對表的一些列進行全文索引,但是jpa是不帶全文索引創建的功能,所以自己寫了一個創建全文索引注解

來自動創建全文索引,需要配合spring 或 springboot使用,一定要spring掃描到如下類:

ApplicationUtil.java
ScanSupport.java
FulltextDelegator.java

 

1.獲取spring環境的上下文

package cc.zeelan.mall.common.application;

import java.util.Objects;

import javax.persistence.EntityManager;

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

/**
 * 獲取spring-context上下文環境和EntityManager相關對象
 * 
 * @project common-utils
 * @fileName ApplicationUtil.java
 * @Description
 * @author light-zhang
 * @date 2019年4月29日
 * @version 1.0.0
 */
@Configuration
public class ApplicationUtil implements ApplicationContextAware {
    /**
     * 事務超時時間
     */
    public static final int TransactionalTimeOut = 3000;
    /**
     * 上下文環境
     */
    private static ApplicationContext applicationContext;
    /**
     * jpa對象管理器
     */
    private static EntityManager entityManager;

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

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @Autowired
    public ApplicationUtil(EntityManager entityManager) {
        ApplicationUtil.entityManager = entityManager;
    }

    public static EntityManager getEntityManager() {
        return entityManager;
    }

    /**
     * 關閉實體管理
     */
    public static void closeEntityManager() {
        if (!Objects.isNull(ApplicationUtil.getEntityManager())) {
             getEntityManager().close();
        }
    }
}

 

2.掃描指定包下面的所有類信息

/**
 * SPRING掃描包下面的類
 * 
 * @project common-utils
 * @fileName Scaner.java
 * @Description
 * @author light-zhang
 * @date 2019年5月5日
 * @version 1.0.0
 */
public class ScanSupport implements ResourceLoaderAware {
    /**
     * 注入ResourceLoader讓Spring管理
     */
    private ResourceLoader resourceLoader;

    private ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
    private static final String FULLTEXT_SACN_PACKAGE_PATH = "fulltext.scan.package";

    /**
     * set注入對象
     */
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    /**
     * 利用spring提供的掃描包下面的類信息,再通過classfrom反射獲得類信息
     * 
     * @param scanPath
     * @return
     * @throws IOException
     */
    public Set<Class<?>> doScan(String scanPath) throws IOException {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                .concat(ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(scanPath))
                        .concat("/**/*.class"));
        Resource[] resources = resolver.getResources(packageSearchPath);
        MetadataReader metadataReader = null;
        for (Resource resource : resources) {
            if (resource.isReadable()) {
                metadataReader = metadataReaderFactory.getMetadataReader(resource);
                try {
                    if (metadataReader.getClassMetadata().isConcrete()) {// 當類型不是抽象類或接口在添加到集合
                        classes.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return classes;
    }

    /**
     * 指定包下面的類信息
     * 
     * @return 多個類信息
     */
    public static Set<Class<?>> classInfos() {
        try {
            String scanPath = ApplicationUtil.getApplicationContext().getEnvironment()
                    .getProperty(FULLTEXT_SACN_PACKAGE_PATH);
            return new ScanSupport().doScan(scanPath);
        } catch (Exception e) {
            Assert.RuntimeException("掃描包類信息錯誤");
        }
        return null;
    }
}

 

 

 

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.Objects;
import java.util.Set;

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

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.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import cc.zeelan.mall.common.application.ApplicationUtil;

/**
 * 全文索引注解實現
 * 
 * @作者 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 {
    @Override
    public void run(String... args) throws Exception {
        this.getAnnotation(ScanSupport.classInfos());
    }

    /**
     * 處理全文注解相關
     * 
     * @param clazz
     */
    public void getAnnotation(Set<Class<?>> clazzs) {
        String indexName = null;
        for (Class<?> fclass : clazzs) {
            Field[] fields = fclass.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Fulltext.class)) {
                    Table table = fclass.getAnnotation(Table.class);
                    if (Objects.isNull(table) || StringUtils.isEmpty(table.name())) {
                        Assert.notNull(table, "@table(name='') specified for entity");
                    }
                    Fulltext fulltextKey = field.getAnnotation(Fulltext.class);
                    if (!Objects.isNull(fulltextKey) && !StringUtils.isEmpty(fulltextKey.IndexesName())) {// 用戶自定義的全文索引名稱
                        indexName = fulltextKey.IndexesName();
                    } else {// 策略創建的索引index名稱
                        indexName = table.name().concat("_").concat(fulltextKey.columnName()).concat("_idx");
                    }
                    if (isExist(table.name(), indexName)) {// 當前索引不存在在創建索引,存在跳過
                        createIndex(table.name(), indexName, fulltextKey.columnName());
                    }
                }
            }
        }
        ApplicationUtil.closeEntityManager();
    }

    /**
     * 創建全文索引
     */
    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(")");
        createIndex.append(" WITH PARSER ngram");// 采用MYSQ Lngram全文解析器
        excute(createIndex.toString());// 在執行創建全文索引操作
        createIndex.setLength(0);// 清空
    }

    /**
     * 刪除索引
     */
    public 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 boolean 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("%'"));
        boolean result = excuteQuery(existIndex.toString());
        existIndex.setLength(0);
        return result;
    }

    /**
     * CURD執行器
     * 
     * @param sql
     * @return
     */
    @Modifying(clearAutomatically = true)
    private int excute(String sql) {
        Query query = ApplicationUtil.getEntityManager().createNativeQuery(sql);
        return query.executeUpdate();
    }

    /**
     * 查詢執行器
     * 
     * @param sql
     * @return
     */
    private boolean excuteQuery(String sql) {
        Query query = ApplicationUtil.getEntityManager().createNativeQuery(sql);
        return CollectionUtils.isEmpty(query.getResultList());
    }
}

 

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;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM