zoukankan      html  css  js  c++  java
  • Spring Data JPA: 实现自定义Repository

    一、前言

      由于项目中的 实体(entity)默认都是继承一个父类(包含一些公共的属性,比如创建时间,修改时间,是否删除,主键id)。为了实现逻辑删除,一般会自己实现RepositoryFactoryBean 和 Repository。但是由于多个团队开发的结果,表的结构没有同一,也就是会出现有的表没有基础父类对应的字段,这样就会导致自定义的jpa repository操作这些表就会出错。

    二、最开始实现

      默认父类

    import java.io.Serializable;
    import java.sql.Timestamp;
    
    import javax.persistence.Column;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.MappedSuperclass;
    
    import org.hibernate.annotations.GenericGenerator;
    
    @MappedSuperclass
    public abstract class AbsIdEntity implements Serializable {
    
        private static final long serialVersionUID = 7988377299341530426L;
    
        @Id
        @GenericGenerator(name="uuid", strategy="uuid")
        @GeneratedValue(generator="uuid")
        @Column(name="id")
        protected String id;
    
        @Column(name = "creationtime")
        protected Timestamp creationTimestamp = new Timestamp(System.currentTimeMillis());
        @Column(name = "lastmodifiedtime")
        protected Timestamp modificationTimestamp = new Timestamp(System.currentTimeMillis());
        
        @Column(name = "dr")
        protected int dr;// 是否删除。0:未删除;1:已删除
    
        /**
         * 主键,对应id字段
         */
        public String getId() { return id; }
        public void setId(String id) { this.id = id; }
    
        /**
         * 创建日期,对应ts_insert字段
         */
        public Timestamp getCreationTimestamp() { return creationTimestamp; }
        public void setCreationTimestamp(Timestamp creationTimestamp) { this.creationTimestamp = creationTimestamp; }
    
        /**
         * 修改日期,对应ts_update字段
         */
        public Timestamp getModificationTimestamp() { return modificationTimestamp; }
        public void setModificationTimestamp(Timestamp modificationTimestamp) { this.modificationTimestamp = modificationTimestamp; }
    
        /**
         * 是否删除,对应dr字段
         * @return
         */
        public int getDr() {
            return dr;
        }
        public void setDr(int dr) {
            this.dr = dr;
        }
    
    }
    View Code

      自定义Repository接口

    • 添加BaseDao接口
    • BaseDao继承了JpaSpecificationExecutor、CrudRepository,这样可以保证所有Repository都有基本的增删改查以及分页等方法。
    • 在BaseDao上添加@NoRepositoryBean标注,这样Spring Data Jpa在启动时就不会去实例化BaseDao这个接口
    import java.io.Serializable;
    
    import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
    import org.springframework.data.repository.CrudRepository;
    import org.springframework.data.repository.NoRepositoryBean;
    
    /**
     * Data Access Object基类,已经包含了常用的增删改查操作。<br>
     * 使用时只需要继承接口,不需要实现类,spring自动通过cglib生成实现类
     * 
     * @param <T>
     *            实体类型
     */
    @NoRepositoryBean
    public interface BaseDao<T extends AbsIdEntity> extends
            CrudRepository<T, Serializable>/* JpaRepository<T, Serializable> */, JpaSpecificationExecutor<T> {
    }
    View Code

      然后,使所有Repository接口都继承BaseDao

      实现BaseRepository

      定义好自定义的方法后,我们现在通过一个基本的Repository类来实现该方法:

      首先添加BaseDaoImpl类,继承SimpleJpaRepository类,使其拥有Jpa Repository的基本方法。

      我们发现Repository有两个构造函数:

    • SimpleJpaRepository(JpaEntityInformation entityInformation, EntityManager entityManager)
    • SimpleJpaRepository(Class domainClass, EntityManager em)

      这里我们实现第二个构造函数,拿到domainClassEntityManager两个对象。因为我们要实现的是知道某个Repository是否支持某个领域对象的类型,因此在实现构造函数时我们将domainClass的信息保留下来。

    import java.io.Serializable;
    import java.sql.Timestamp;
    
    import javax.persistence.EntityManager;
    
    import org.springframework.data.jpa.repository.support.JpaEntityInformation;
    import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.yyjz.icop.base.dao.BaseDao;
    
    @Transactional
    public class BaseDaoImpl<T extends AbsIdEntity> extends SimpleJpaRepository<T, Serializable> implements BaseDao<T> {
    
        @SuppressWarnings("unused")
        private final EntityManager entityManager;
    
        public BaseDaoImpl(Class<T> domainClass, EntityManager entityManager) {
            super(domainClass, entityManager);
            this.entityManager = entityManager;
        }
    
        public BaseDaoImpl(JpaEntityInformation<T, Serializable> information, EntityManager entityManager) {
            super(information, entityManager);
            this.entityManager = entityManager;
        }
    
        @Override
        public <S extends T> S save(S entity) {
            entity.setModificationTimestamp(new Timestamp(System.currentTimeMillis()));
            return super.save(entity);
        }
    
        /**
         * 只做逻辑删除
         */
        @Override
        public void delete(T entity) {
            entity.setDr(1);
            save(entity);
        }
    
        @Override
        public void delete(Serializable id) {
            T entity = findOne(id);
            entity.setDr(1);
            this.save(entity);
        }
    
    }
    View Code

      RepositoryFactoryBean 实现

      接下来我们来创建一个自定义的BaseDaoFactoryBean来代替默认的RepositoryFactoryBeanRepositoryFactoryBean负责返回一个RepositoryFactory,Spring Data Jpa 将使用RepositoryFactory来创建Repository具体实现,这里我们用BaseDaoImpl代替SimpleJpaRepository作为Repository接口的实现。这样我们就能够达到为所有Repository添加或修改自定义方法的目的。

    import xxx.AbsIdEntity;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
    import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
    import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
    import org.springframework.data.repository.core.RepositoryInformation;
    import org.springframework.data.repository.core.RepositoryMetadata;
    import org.springframework.data.repository.core.support.RepositoryFactorySupport;
    
    import javax.persistence.EntityManager;
    import java.io.Serializable;
    
    public class BaseDaoFactoryBean<R extends JpaRepository<T, Serializable>, T extends AbsIdEntity> extends JpaRepositoryFactoryBean<R, T, Serializable> {
    
        @Override
        protected RepositoryFactorySupport createRepositoryFactory(final EntityManager entityManager) {
            return new JpaRepositoryFactory(entityManager) {
    
                protected SimpleJpaRepository<T, Serializable> getTargetRepository(
                        RepositoryInformation information,    EntityManager entityManager) {
                    return new BaseDaoImpl((Class<T>) information.getDomainType(), entityManager);
                }
    
                @Override
                protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
                    return BaseDaoImpl.class;
                }
            };
        }
    }
    View Code

      jpa 配置文件

    <!-- Spring Data Jpa配置 -->
    <jpa:repositories base-package="com.xxx"
        transaction-manager-ref="transactionManager"
        entity-manager-factory-ref="entityManagerFactory"
        factory-class="xxx.BaseDaoFactoryBean"><!-- 自定义RepositoryFactoryBean -->
    </jpa:repositories>

    三、改进之后

      由于有的表没有默认父类AbsIdEntity对应的字段,导致生成 Repository 在操作表的时候会报错。需要修改的就是RepositoryFactoryBean的实现逻辑。对于继承了AbsIdEntity的实体类,返回自定义的BaseRepository(也就是BaseDaoImpl),否则就返回SimpleJpaRepository。注意自定义RepositoryFactoryBean的泛型也做了修改。

    import xxx.AbsIdEntity;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
    import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
    import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
    import org.springframework.data.repository.core.RepositoryInformation;
    import org.springframework.data.repository.core.RepositoryMetadata;
    import org.springframework.data.repository.core.support.RepositoryFactorySupport;
    
    import javax.persistence.EntityManager;
    import java.io.Serializable;
    
    public class BaseDaoFactoryBean<R extends JpaRepository<T, Serializable>, T> extends JpaRepositoryFactoryBean<R, T, Serializable> {
    
        @Override
        protected RepositoryFactorySupport createRepositoryFactory(final EntityManager entityManager) {
            return new JpaRepositoryFactory(entityManager) {
    
                protected SimpleJpaRepository<T, Serializable> getTargetRepository(
                        RepositoryInformation information,    EntityManager entityManager) {
                    Class<T> domainClass = (Class<T>) information.getDomainType();
                    if(AbsIdEntity.class.isAssignableFrom(domainClass)) {
                        return new BaseDaoImpl(domainClass, entityManager);
                    } else { 
                        return new SimpleJpaRepository(domainClass, entityManager);
                    }
                }
    
                @Override
                protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
                    return metadata.getDomainType().isAssignableFrom(AbsIdEntity.class) ? BaseDaoImpl.class : SimpleJpaRepository.class;
                }
            };
        }
    }
    View Code

      至此,完成了适配。

     生活不止眼前的bug,还有诗和远方。。。

  • 相关阅读:
    scala程序启动时,Could not locate executable nullinwinutils.exe in the Hadoop binaries解决方案
    binlog_format的模式有哪几种?各自的特点是?
    解决IDEA控制台junit不能用Scanner输入问题
    【转】什么是乐观锁,什么是悲观锁
    lyt经典版MySQL基础——流程控制结构
    ambari 2.5.0源码编译安装
    linux(centeros)svn的安装
    机器学习
    算法思想整理
    lucene
  • 原文地址:https://www.cnblogs.com/hujunzheng/p/6494671.html
Copyright © 2011-2022 走看看