SherryLang / javalearning

while(Java){ learn !! }
3 stars 1 forks source link

Spring JPA自定义实现(孔乙己的茴香豆) #13

Open SherryLang opened 7 years ago

SherryLang commented 7 years ago

主要目的

获取EntityManager的实例,使用实例的方法来实现creatQuery

第一种实现:最原始的方式

package com.xxxxx.repository.base.impl;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import com.xxxxx.repository.base.BasicRepository;
import com.xxxxx.support.GenericsTool;

/** 
 * 对外相当于simpleJpaRepository的功能
 * implements BasicRepository<T, ID>的部分可以不要,并且去掉@Override注解,相当于JPA的方法全部使用em自定义实现
*/
public class BasicDAO<T, ID extends Serializable> implements BasicRepository<T, ID>{

    @PersistenceContext
    protected EntityManager entityManager;

    private BasicRepository<T, ID> basicRepository;

    @SuppressWarnings("unchecked")
    private BasicRepository<T, ID> getBasicRepository() {
        if (null == basicRepository) {
            //通过反射,Class声明的范型参数的类型,用于实例化BasicRepositoryImpl对象,效果同Factory 
            basicRepository = new BasicRepositoryImpl<T, ID>(
                    (Class<T>) GenericsTool.getSuperClassGenricType(getClass()), entityManager);
        }
        return basicRepository;
    }

    @Override
    public List<T> findAll() {
        return getBasicRepository().findAll();
    }

    @Override
        // ...(父类的方法全部Override一遍)

}

其中用到的工具类方法如下:

package com.xxxxx.constant;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 反射转换对象什么的泛型参数类型
 */
public class GenericsTool {

    private static final Logger log = LoggerFactory.getLogger(GenericsTool.class);

    /**
     * 通过反射,获得定义Class时声明的父类的范型参数的类型
     * @param clazz 类对象
     * @return 泛型类型
     */
    public static Class<?> getSuperClassGenricType(Class<?> clazz) {
        return getSuperClassGenricType(clazz, 0);
    }

    /**
     * 通过反射,获得定义Class时声明的父类的范型参数的类型
     * @param clazz 类对象
     * @param index 泛型位置,从0开始
     * @return 泛型类型
     */
    public static Class<?> getSuperClassGenricType(Class<?> clazz, int index) {
        Type genType = clazz.getGenericSuperclass();
        if (!(genType instanceof ParameterizedType)) {
            log.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
            return Object.class;
        }
        Type[] params = ((ParameterizedType)genType).getActualTypeArguments();
        if (index >= params.length || index < 0) {
            log.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + params.length);
            return Object.class;
        }
        if (!(params[index] instanceof Class<?>)) {
            log.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
            return Object.class;
        }
        return (Class<?>)params[index];
    }
}

第二种实现:Factory构造Bean,然而在接口中暴露了函数体

6月9日白天的代码 《深入实践Spring Boot》P83:自定义的接口必须在程序启动时装配,才能正常使用……

第三种实现:实现CccPageRepository接口,避免使用default修饰

CccPageRepository 接口继承自JpaRepository,只要需要定义方法名(避免暴露函数体),并且使用@NoRepositoryBean注解:

package com.xxxx.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean;

import com.xxxx.domain.CccPage;
@NoRepositoryBean
public interface CccPageRepository extends JpaRepository<CccPage, Long>{
    List<CccPage> findByDocNo(String docNo);
    List<CccPage> searchCccByKeyword(String fieldName, String[] keywords);
}

实现类内部定义私有的EntityManager实体,并且用@Autowired注入,用构造函数构造自身(包括父类):

package com.xxxx.repository;

import java.util.List;

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

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.stereotype.Repository;

import com.xxxx.domain.CccPage;

@Repository
public class CccPageRepositoryImpl extends SimpleJpaRepository<CccPage, Long> implements CccPageRepository {

    private String[] fields = { "model", "productName", "docNo", "remarks" };

        //私有成员变量em
    private EntityManager em;

    @Autowired
    public CccPageRepositoryImpl (EntityManager em) {
        super(CccPage.class, em);
        this.em = em;
    }

    @SuppressWarnings("unchecked")
    public List<CccPage> findByDocNo(String docNo) {
        Query query = em.createQuery("from CccPage where docNo=:docNo");
        query.setParameter("docNo", docNo);
        return query.getResultList();
    }

    @SuppressWarnings("unchecked")
    public List<CccPage> searchCccByKeyword(String fieldName, String[] keywords) {
        StringBuilder sqlString = new StringBuilder("");
        //隐藏函数体,主要功能是构造自定义的sqlString
        System.out.println(sqlString.toString());

        Query query = em.createQuery(sqlString.toString());
        List<CccPage> res = query.getResultList();
        return res;
    }

}

第四种实现:反射获取em实例

关键:基类BasicDAO 反射获取EntityManager实例

package com.xxxx.repository.base;

import java.lang.reflect.Field;

import javax.persistence.EntityManager;

import org.slf4j.LoggerFactory;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;

public interface BasicDAO {
    /**
     * 通过SimpleJpaRepository的em属性,反射获取EntityManager的实例
     * 
     * @return
     */
    default EntityManager getEntityManager() {
        try {
            Field f = SimpleJpaRepository.class.getDeclaredField("em");
            f.setAccessible(true);
            return (EntityManager) f.get(this);
        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
            LoggerFactory.getLogger(BasicDAO.class)
                    .error("not get current entityManager from reflect SimpleJpaRepository's field", e);
        }
        return null;
    }
}

CccPageRepository 继承JpaRepository和基类BasicDAO

package com.xxxx.repository;

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean;

import com.xxxx.domain.CccPage;
import com.xxxx.repository.base.BasicDAO;

/* 注意这里要继承BasicDAO!*/
@NoRepositoryBean
public interface CccPageRepository extends JpaRepository<CccPage, Long>, BasicDAO {

    List<CccPage> findByDocNo(String docNo);

    List<CccPage> searchCccByKeyword(String fieldName, String[] keywords);
}

实现类CccPageRepositoryImpl

package com.xxxx.repository;

import java.util.List;

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

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.stereotype.Repository;

import com.xxxx.domain.CccPage;

@Repository
public class CccPageRepositoryImpl extends SimpleJpaRepository<CccPage, Long> implements CccPageRepository {

    private String[] fields = { "model", "productName", "docNo", "remarks" };

    /*
     * private EntityManager em;
     */
    public CccPageRepositoryImpl (EntityManager em) {
        super(CccPage.class, em);
    }

    @SuppressWarnings("unchecked")
    public List<CccPage> findByDocNo(String docNo) {
        Query query = getEntityManager().createQuery("from CccPage where docNo=:docNo");
        query.setParameter("docNo", docNo);
        return query.getResultList();
    }

    @SuppressWarnings("unchecked")
    public List<CccPage> searchCccByKeyword(String fieldName, String[] keywords) {
        StringBuilder sqlString = new StringBuilder("");
        //隐藏函数体,主要功能是构造自定义的sqlString
        System.out.println(sqlString.toString());

        Query query = getEntityManager().createQuery(sqlString.toString());
        List<CccPage> res = query.getResultList();
        return res;
    }
}