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,还有诗和远方。。。

  • 相关阅读:
    每日一篇文献:Robotic pick-and-place of novel objects in clutter with multi-affordance grasping and cross-domain image matching
    每日一篇文献:Intuitive Bare-Hand Teleoperation of a Robotic Manipulator Using Virtual Reality and Leap Motion
    每日一篇文献:Virtual Kinesthetic Teaching for Bimanual Telemanipulation
    HEBI Robotic Arm VR Teleoperation
    「iQuotient Case」AR device teleoperated robotic arm
    VR and Digital Twin Based Teleoperation of Robotic Arm
    HEBI Robotic Arm VR Teleoperation
    Human Robot Interaction
    Immersive Teleoperation Project
    机器人演示学习
  • 原文地址:https://www.cnblogs.com/hujunzheng/p/6494671.html
Copyright © 2011-2022 走看看