Reputation: 723
I'm using hibernate with generics to implement some feature. Here I have two entities Country and CountryTrans. CountryTrans represent different translations (different name) for Country. I'm using Spring Boot for this project.
I have a top level class that contains the Id.
@MappedSuperclass
public abstract class AbstractEntity {
private Long id;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Then MultiLingualObject generic class that contains the type of translation.
@MappedSuperclass
public abstract class MultiLigualObject<TYPE extends TranslationObject>
extends AbstractEntity {
protected List<TYPE> transInfo = new ArrayList<>();
public abstract List<TYPE> getTransInfo();
public void setTransInfo(List<TYPE> transInfo) {
this.transInfo = transInfo;
}
}
TranslationObject class is representing the entity translation.
@MappedSuperclass
public abstract class TranslationObject extends AbstractEntity {
private String langCode;
@Column(name = "lang_code")
public String getLangCode() {
return langCode;
}
public void setLangCode(String langCode) {
this.langCode = langCode;
}
}
Finally the Country class..
@Entity
@Table(name = "country")
public class Country extends MultiLigualObject<CountryTrans> {
private String countryCode;
private String alternateCode;
@Column(name = "country_code", nullable = false, length = 2)
public String getCountryCode() {
return this.countryCode;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
@Column(name = "alternate_code", length = 3)
public String getAlternateCode() {
return this.alternateCode;
}
public void setAlternateCode(String alternateCode) {
this.alternateCode = alternateCode;
}
@OneToMany(fetch = FetchType.EAGER, mappedBy = "country")
@Override
public List<CountryTrans> getTransInfo() {
return this.transInfo;
}
}
And CountryTrans class.
@Entity
@Table(name = "country_trans")
public class CountryTrans extends TranslationObject {
private String name;
private Country country;
@Column(nullable = false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne
@JoinColumn(name = "country_id")
public Country getCountry() {
return country;
}
public void setCountry(Country country) {
this.country = country;
}
}
In order to test this I wrote a simple test case.
@Test
public void testCountry() throws Exception {
Country country = countryRepository.findByCountryCode("AD");
}
When I run this test case it gives following exception.
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:1249)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.access$600(EntityManagerFactoryBuilderImpl.java:120)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:860)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:425)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:849)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:343)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:318)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
... 43 more
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.List, at table: country, for columns: [org.hibernate.mapping.Column(trans_info)]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:349)
at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:322)
at org.hibernate.mapping.Property.isValid(Property.java:241)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:496)
at org.hibernate.mapping.RootClass.validate(RootClass.java:270)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1360)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1851)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857)
... 51 more
Some basic search gives this is an issue with field level access vs property access. But in here I implemented this in a property level access. But not sure why it is failing. Any idea ?
Adding CountryRepository
@Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
Country findByCountryCode(String countryCode);
}
CREATE TABLE country (
id bigint(20) NOT NULL AUTO_INCREMENT,
alternate_code varchar(3) DEFAULT NULL,
country_code varchar(2) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE country_trans (
id bigint(20) NOT NULL AUTO_INCREMENT,
lang_code varchar(255) DEFAULT NULL,
country_id bigint(20) DEFAULT NULL,
name varchar(255) DEFAULT NULL,
PRIMARY KEY (id),
CONSTRAINT FK_COUNTRY_TRAN_ID FOREIGN KEY (country_id) REFERENCES country (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
Upvotes: 2
Views: 446
Reputation: 6962
Hibernate (or JPA) will use all properties of the class, unless you specifically mark them with @Transient. It is trying to figure out what column in the db transInfo
maps to. The default is to look for a column in the Table with the same name as the parameter (transInfo) and is unable to find it. You can fix this by telling it to ignore the parameter.
Example:
@Transient
private List<CountryTrans> transInfo;
Upvotes: 1