#StackBounty: #java #spring-boot #spring-data-rest Spring Data Rest with @Version from Parent-Entity

Bounty: 250

I set up a @Repository for an Entity with a @Version property and published it via @RepositoryRestResource which was pretty straight forward. The version is transfered to the and from the client in the Http Header as an ETag and it works perfectly.

In my current scenario I have a child Entity which doesn’t have a @Version-Tag itself, instead the @Version-Tag of its parent has to be used. That means, that every change of the child or the parent has to increment the parent version. Also the operation should fail, if the parent version is outdated. For this case EntityManager offers the LockModeType.OPTIMISTIC_FORCE_INCREMENT.

How do I customize Spring Data Rest to implement such behavior? Do I have to implement a @RestController from scratch?

—————– edit —————-

Here is a small example i set up which works for a simple entity

Application.java

@SpringBootApplication
public class Application
{
    public static void main(String[] args)
    {
        SpringApplication.run(Application.class, args);
    }

    @Configuration
    @EnableJpaRepositories
    public static class ServerConfiguration
    {
        private EntityManagerFactory emf;
    
        @Bean
        public EntityManagerFactory entityManagerFactory()
        {
            if (emf == null)
            {
                Map<String, Object> properties = new HashMap<>();
                properties.put(AvailableSettings.CLASSLOADERS, Collections.singleton(Thread.currentThread().getContextClassLoader()));
                emf =  Persistence.createEntityManagerFactory("test", properties);
            }
            return emf;
        }

        @Bean
        public PlatformTransactionManager transactionManager()
        {
            JpaTransactionManager transactionManager = new JpaTransactionManager(entityManagerFactory());
            return transactionManager;
        }
    
        @Bean
        public ShallowEtagHeaderFilter shallowEtagHeaderFilter() 
        {
            return new ShallowEtagHeaderFilter();
        }
    }
}

SimpleRepository.java

@Repository
@RepositoryRestResource(collectionResourceRel = "simples", path = "simples")
public interface SimpleRepository  extends JpaRepository<Simple, Long>
{
}

Simple.java

@Data
@NoArgsConstructor
@Entity
@Table(name = "SIMPLE")
public class Simple
{
    @JsonCreator
    public Simple(@JsonProperty("changeable") String changeable)
    {
        this.changeable = changeable;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ID")
    @SequenceGenerator(name = "ID", sequenceName = "ID", allocationSize = 1)
    @Column(name = "ID")
    private Long id;
    
    @Version
    @Column(name = "VERSION")
    private int version;
    
    @Column(name = "CHANGEABLE")
    private String changeable;
}

It works perfectly with just these three classes, I can interact with the exposed JPA repository via postman, the version number is shipped in the http header as an ETag.

Now I want to extend it for a parent-child-relationship where the version is only incremented in the parent entity.

Parent.java

@Data
@NoArgsConstructor
@Entity
@Table(name = "PARENT")
public class Parent
{
    @JsonCreator
    public Parent(@JsonProperty("changeable") String changeable)
    {
        this.changeable = changeable;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PARENT_ID")
    @SequenceGenerator(name = "PARENT_ID", sequenceName = "PARENT_ID", allocationSize = 1)
    @Column(name = "PARENT_ID")
    private Long id;
    
    @Version
    @Column(name = "VERSION")
    private int version;
    
    @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private Collection<Child> children = new LinkedHashSet<>();
    
    @Column(name = "CHANGEABLE")
    private String changeable;
}

Child.java

@Data
@NoArgsConstructor
@Entity
@Table(name = "CHILD")
public class Child
{
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CHILD_ID")
    @SequenceGenerator(name = "CHILD_ID", sequenceName = "CHILD_ID", allocationSize = 1)
    @Column(name = "CHILD_ID")
    private Long id;
    
//  NO VERSION! Parent version should be incremented on change  
//  @Version
//  @Column(name = "VERSION")
//  private int version;
    
    @ManyToOne(optional = false)
    @JoinColumn(name = "PARENT_ID", referencedColumnName = "PARENT_ID")
    private Parent parent;
    
    @Column(name = "CHANGEABLE")
    private String changeable;
}

You can find the full example on http://github.com/Meini777/example

What is the best way to implement it? I could code a @RestController on my own instead of using the implicit one from @RepositoryRestResource, but in this case i have implement all the behavior including HATEOAS and ALPS.


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.