43

我有许多需要持久化到数据库的简单对象类型。我正在使用 Spring JPA 来管理这种持久性。对于每种对象类型,我需要构建以下内容:

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

public interface FacilityRepository extends JpaRepository<Facility, Long> {
}


public interface FacilityService {
    public Facility create(Facility facility);
}

@Service
public class FacilityServiceImpl implements FacilityService {

    @Resource
    private FacilityRepository countryRepository;

    @Transactional
    public Facility create(Facility facility) {
        Facility created = facility;
        return facilityRepository.save(created);
    }
}

我突然想到,可以用三个基于泛型的类替换每个对象类型的多个类,从而节省大量样板代码。我不完全确定如何去做,事实上这是否是个好主意?

4

3 回答 3

97
于 2013-10-18T06:30:02.530 回答
26

这是很有可能的!我可能迟到了。但这肯定会在将来对某人有所帮助。这是一个完整的解决方案,就像一个魅力!

为您的实体创建BaseEntity类,如下所示:

@MappedSuperclass
public class AbstractBaseEntity implements Serializable{

    @Id @GeneratedValue
    private Long id;
    @Version
    private int version;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;

    public AbstractBaseEntity() {
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }

    // getters and setters      
}

为您的 DAO 持久性创建一个通用 JPA 存储库接口,如下所示:注意。请记住放置,@NoRepositoryBean以便 JPA 不会尝试为存储库查找实现!

@NoRepositoryBean
public interface AbstractBaseRepository<T extends AbstractBaseEntity, ID extends Serializable>
extends JpaRepository<T, ID>{
    
}

创建一个使用上述基本 JPA 存储库的基本服务类。这是您域中的其他服务接口将简单扩展的接口,如下所示:

public interface AbstractBaseService<T extends AbstractBaseEntity, ID extends Serializable>{
    public abstract T save(T entity);
    public abstract List<T> findAll(); // you might want a generic Collection if u prefer

    public abstract Optional<T> findById(ID entityId);
    public abstract T update(T entity);
    public abstract T updateById(T entity, ID entityId);   
    public abstract void delete(T entity);
    public abstract void deleteById(ID entityId);
    // other methods u might need to be generic
    
}

然后为基本 JPA 存储库创建一个抽象实现,并且还将提供基本 CRUD 方法的实现,如下所示:

@Service
@Transactional
public abstract class AbstractBaseRepositoryImpl<T extends AbstractBaseEntity, ID extends Serializable>
        implements AbstractBaseService<T, ID>{
    
    private AbstractBaseRepository<T, ID> abstractBaseRepository;
    
    @Autowired
    public AbstractBaseRepositoryImpl(AbstractBaseRepository<T, ID> abstractBaseRepository) {
        this.abstractBaseRepository = abstractBaseRepository;
    }
    
    @Override
    public T save(T entity) {
        return (T) abstractBaseRepository.save(entity);
    }

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

    @Override
    public Optional<T> findById(ID entityId) {
        return abstractBaseRepository.findById(entityId);
    }

    @Override
    public T update(T entity) {
        return (T) abstractBaseRepository.save(entity);
    }

    @Override
    public T updateById(T entity, ID entityId) {
        Optional<T> optional = abstractBaseRepository.findById(entityId);
        if(optional.isPresent()){
            return (T) abstractBaseRepository.save(entity);
        }else{
            return null;
        }
    }

    @Override
    public void delete(T entity) {
        abstractBaseRepository.delete(entity);
    }

    @Override
    public void deleteById(ID entityId) {
        abstractBaseRepository.deleteById(entityId);
    }

}

如何使用上述摘要entity、、、servicerepositoryimplementation

这里的示例将是一个MyDomain实体。创建一个扩展AbstractBaseEntity如下的域实体:NB。ID, createdAt, updatedAt,version等将自动包含在MyDomain实体中AbstractBaseEntity

@Entity
public class MyDomain extends AbstractBaseEntity{

    private String attribute1;
    private String attribute2;
    // getters and setters
}

然后为扩展如下repositoryMyDomain实体创建一个:AbstractBaseRepository

@Repository
public interface MyDomainRepository extends AbstractBaseRepository<MyDomain, Long>{

}

另外,为实体创建一个service接口,MyDomain如下所示:

public interface MyDomainService extends AbstractBaseService<MyDomain, Long>{

}

然后为MyDomain扩展实现的实体提供一个实现AbstractBaseRepositoryImpl,如下所示:

@Service
@Transactional
public class MyDomainServiceImpl extends AbstractBaseRepositoryImpl<MyDomain, Long> 
        implements MyDomainService{
    private MyDomainRepository myDomainRepository;

    public MyDomainServiceImpl(MyDomainRepository myDomainRepository) {
        super(myDomainRepository);
    }
    // other specialized methods from the MyDomainService interface

}
Now use your `MyDomainService` service in your controller as follows: 

@RestController // or @Controller
@CrossOrigin
@RequestMapping(value = "/")
public class MyDomainController {
    
    private final MyDomainService myDomainService;

    @Autowired
    public MyDomainController(MyDomainService myDomainService) {
        this.myDomainService = myDomainService;
    }
   
    @GetMapping
    public List<MyDomain> getMyDomains(){
        return myDomainService.findAll();
    }   
    // other controller methods

}

注意。确保AbstractBaseRepository使用注释,@NoRepositoryBean这样JPA就不会尝试找到 bean 的实现。也AbstractBaseServiceImpl必须标记为抽象,否则 JPA 将尝试自动装配AbstractBaseRepository导致 a 的类的构造函数中的所有子 daos,NoUniqueBeanDefinitionException因为创建 bean 时将注入超过 1 个 daos(存储库)!现在您的service,repositoryimplementations更可重用。我们都讨厌样板!

希望这可以帮助某人。

于 2020-04-23T10:11:33.037 回答
0

我正在做一个项目,用spring数据为cassandra创建通用存储库。

首先使用代码创建一个存储库接口。

StringBuilder sourceCode = new StringBuilder();
sourceCode.append("import org.springframework.boot.autoconfigure.security.SecurityProperties.User;\n");
sourceCode.append("import org.springframework.data.cassandra.repository.AllowFiltering;\n");
sourceCode.append("import org.springframework.data.cassandra.repository.Query;\n");
sourceCode.append("import org.springframework.data.repository.CrudRepository;\n");
sourceCode.append("\n");
sourceCode.append("public interface TestRepository extends CrudRepository<Entity, Long> {\n");
sourceCode.append("}");

编译代码得到类,我用的是org.mdkt.compiler.InMemoryJavaCompiler

ClassLoader classLoader = org.springframework.util.ClassUtils.getDefaultClassLoader();
compiler = InMemoryJavaCompiler.newInstance();
compiler.useParentClassLoader(classLoader);
Class<?> testRepository = compiler.compile("TestRepository", sourceCode.toString());

并在 spring 数据运行时初始化存储库。这有点棘手,因为我调试 SpringData 代码以查找它如何在 spring 中初始化存储库接口。

CassandraSessionFactoryBean bean = context.getBean(CassandraSessionFactoryBean.class);
RepositoryFragments repositoryFragmentsToUse = (RepositoryFragments) Optional.empty().orElseGet(RepositoryFragments::empty); 
CassandraRepositoryFactory factory = new CassandraRepositoryFactory(
    new CassandraAdminTemplate(bean.getObject(), bean.getConverter()));
factory.setBeanClassLoader(compiler.getClassloader());
Object repository = factory.getRepository(testRepository, repositoryFragmentsToUse);

现在您可以尝试存储库的保存方法,也可以尝试其他方法,例如 findById。

Method method = repository.getClass().getMethod("save", paramTypes);
T obj = (T) method.invoke(repository, params.toArray());

我在这个 repo https://github.com/maye-msft/generic-repository-springdata中放了一个完整的示例代码和实现。

您可以使用类似的逻辑将其扩展到 JPA。

于 2019-07-17T15:59:49.110 回答