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; } }
自定义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> { }
然后,使所有Repository接口都继承BaseDao
实现BaseRepository
定义好自定义的方法后,我们现在通过一个基本的Repository类来实现该方法:
首先添加BaseDaoImpl类,继承SimpleJpaRepository类,使其拥有Jpa Repository的基本方法。
我们发现Repository有两个构造函数:
- SimpleJpaRepository(JpaEntityInformation entityInformation, EntityManager entityManager)
- SimpleJpaRepository(Class domainClass, EntityManager em)
这里我们实现第二个构造函数,拿到domainClass
和EntityManager
两个对象。因为我们要实现的是知道某个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); } }
RepositoryFactoryBean 实现
接下来我们来创建一个自定义的BaseDaoFactoryBean来代替默认的RepositoryFactoryBean
。RepositoryFactoryBean
负责返回一个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; } }; } }
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; } }; } }
至此,完成了适配。
生活不止眼前的bug,还有诗和远方。。。
本文来自博客园,作者:hjzqyx,转载请注明原文链接:https://www.cnblogs.com/hujunzheng/p/6494671.html