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, ?, ?, ?, ?, ?, ?)
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.
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).
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With