Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheritance and @AttributeOverride on @Id field

I have one of my entity where I need to set manually the ID (PK). I have some Abstract @MappedSuperclass used for Audit and PK and I want still to use it. So my idea was to override the id column to get rid off the @GeneratedValue(strategy = GenerationType.AUTO.

So I've got something like this:

@Entity
@Table(name = Constants.MERCHANT_PREFIX + "MERCHANT")
@Cacheable(false)
public class Merchant extends AbstractAuditable<String, Long> {
    @AttributeOverride(name = "id", column = @Column(name="ID"))
    private Long id;
    @Override
    public Long getId() {
        return id;
    }
    @Override
    public void setId(Long id) {
        this.id = id;
    }
}

@MappedSuperclass
@EntityListeners(value = { AuditingEntityListener.class })
public abstract class AbstractAuditable<U, PK extends Serializable> extends AbstractPersistable<PK> {
...
}

@MappedSuperclass
public abstract class AbstractPersistable<PK extends Serializable> implements Persistable<PK> {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private PK id;
    public PK getId() {
        return id;
    }
    protected void setId(final PK id) {
        this.id = id;
    }
}

It's working fine (server started and with swagger)

But my integration test is failing, i have this error below. Do you know why? I think maybe I should not use @AttributeOverride in that way... And I would like to find a working way with my Merchant class still extending my abstract classes (Audit and PK)

org.springframework.dao.InvalidDataAccessResourceUsageException: 
could not prepare statement; 
        SQL [insert into MER_MERCHANT (ID, CREATED_BY, CREATED_DATE, LAST_MODIFIED_BY, LAST_MODIFIED_DATE, ALLOW_ACCESS, id) 
values (default, ?, ?, ?, ?, ?, ?)]; 
        nested exception is org.hibernate.exception.SQLGrammarException: 
could not prepare statement

Unit test:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {ApplicationTest.class})
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
        DirtiesContextTestExecutionListener.class,
        TransactionalTestExecutionListener.class,
        DbUnitTestExecutionListener.class })
@DatabaseSetup(value = "classpath:merchantData.xml")
@DatabaseTearDown(value="classpath:merchantData.xml", type = DatabaseOperation.DELETE_ALL)
public class RestMerchantControllerTest extends AbstractControllerTest {

    @Test
    public void testCreateMerchant() throws Exception {
        final Long merchantId = 2L;
        final Boolean allowAccess = true;
        MerchantCreateVO merchantCreateVO = StubVOBuilder.buildMerchantCreateVO(allowAccess);
        String requestBody = JsonUtils.jsonFormat(merchantCreateVO);

        mvc.perform(post("/merchants/" + merchantId).contentType(MediaType.APPLICATION_JSON).content(requestBody)).andExpect(status().isOk());
    }

}

And ApplicationTest: (ApplicationTest is very similar to our Application used to set JPA)

@Configuration
@ComponentScan(value = "uk.co.xxx.yyy", excludeFilters = @ComponentScan.Filter(value=Configuration.class, type = FilterType.ANNOTATION ))
@EnableWebMvc
@EnableTransactionManagement
@PropertySources({@PropertySource("classpath:hibernate.properties"), @PropertySource("classpath:junit-persistence.properties"), @PropertySource("classpath:application-test.properties")})
@EnableJpaRepositories(basePackages = "uk.co.xxx.yyy.merchant.data.repository")
@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
public class ApplicationTest {
@Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
...
}
 @Bean
    public JpaTransactionManager transactionManager() {
...
}

}

With the server I've got this insert:

insert 
into
    MER_MERCHANT
    (CREATED_BY, CREATED_DATE, LAST_MODIFIED_BY, LAST_MODIFIED_DATE, ALLOW_ACCESS, id) 
values
    (?, ?, ?, ?, ?, ?)

And with the integration test:

insert 
into
    MER_MERCHANT
    (ID, CREATED_BY, CREATED_DATE, LAST_MODIFIED_BY, LAST_MODIFIED_DATE, ALLOW_ACCESS, id) 
values
    (default, ?, ?, ?, ?, ?, ?)
like image 477
Emilien Brigand Avatar asked Mar 02 '15 13:03

Emilien Brigand


People also ask

How to override attributes from embedded class in hibernate?

Overriding an attribute mapping You can use the @AttributeOverride annotation on the Book entity to override the mapping of each attribute defined by the Publication class. You only need to provide the name of the attribute for which you want to change the mapping and a @Column annotation.

What is@ AttributeOverride in hibernate?

Annotation Type AttributeOverride May be applied to an entity that extends a mapped superclass or to an embedded field or property to override a basic mapping or id mapping defined by the mapped superclass or embeddable class (or embeddable class of one of its attributes).

What is @attributeoverrides?

The @AttributeOverride annotations allow you to override the columns to which the embedded class's properties are mapped. The use case for this is when the embeddable class is used in different situations where its column names differ, and some mechanism is required to let you change those column mappings.


1 Answers

So my idea was to override the id column to get rid off the @GeneratedValue(strategy = GenerationType.AUTO.

The @AttributeOverride annotation is used to signal to JPA that you wish to alter the default mapping of an attribute name to a column name. This is needed if you are developing a JPA application that interoperates with a legacy system whose fields or columns are named differently than yours are named.

From the JavaDoc, @AttributeOverride is...

  • Used to override the mapping of a Basic (whether explicit or default) property or field or Id property or field.

  • May be applied to an entity that extends a mapped superclass or to an embedded field or property to override a basic mapping or id mapping defined by the mapped superclass or embeddable class (or embeddable class of one of its attributes).

Unfortunately, this annotation is effective only on property or field mappings that can be referenced through the @Column API ... and the @Column annotation basically allows you to change only the mapping of the field name. You cannot alter the field type or any other mappings with @AttributeOverride. Therefore, the @GeneratedValue mapping will continue to be inherited by all subclasses of the @MappedSuperclass.

like image 180
scottb Avatar answered Oct 28 '22 01:10

scottb