Hey guys,
So I've been working two days to make something work without success. My goal is fairly simple: make a JPA repository using Spring Data JPA and EclipseLink that is able to query a superclass element (named 'HelpElement') that is implemented by two actual database objects ('HelpTopic' and 'HelpPage').
So I have been reading the web. A lot. And I can't make it work. Important note: everything is working fine using only EclipseLink. As soon as I add Spring Data JPA, i get the following exception that I now dream of during my nightmares.
Caused by: java.lang.IllegalArgumentException: No [EntityType] was found for the key class [com.dalarian.db.entity.help.HelpElement] in the Metamodel - please verify that the [Entity] class was referenced in persistence.xml using a specific <class>com.dalarian.db.entity.help.HelpElement</class> property or a global <exclude-unlisted-classes>false</exclude-unlisted-classes> element.
So here are all the files I use that should be required to see where I have done things wrong.
META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="h t t p://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="default">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>com.dalarian.db.help.HelpTopic</class>
<class>com.dalarian.db.help.HelpPage</class>
<class>com.dalarian.db.help.HelpElement</class>
<properties>
<property name="eclipselink.logging.level" value="INFO"></property>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"></property>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://${database.server}:${database.port}/${database.db_name}"></property>
<property name="javax.persistence.jdbc.password" value="${database.password}"></property>
<property name="javax.persistence.jdbc.user" value="${database.user}"></property>
</properties>
</persistence-unit>
</persistence>
com.dalarian.ApplicationContext
package com.dalarian;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
import javax.persistence.EntityManagerFactory;
/**
* Core application global configuration.
*
*/
@Configuration
@ComponentScan(basePackages = {"com.dalarian.db"})
@EnableJpaRepositories
public class ApplicationContext {
@Bean
public JpaVendorAdapter vendorAdapter() {
EclipseLinkJpaVendorAdapter vendorAdapter = new EclipseLinkJpaVendorAdapter();
vendorAdapter.setDatabasePlatform("org.eclipse.persistence.platform.database.MySQLPlatform");
vendorAdapter.setShowSql(true);
return vendorAdapter;
}
// @Bean
// public DataSource dataSource() {
// EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
// return builder.setType(EmbeddedDatabaseType.HSQL).build();
// }
@Bean
public EntityManagerFactory entityManagerFactory() {
EclipseLinkJpaVendorAdapter vendorAdapter = new EclipseLinkJpaVendorAdapter();
// vendorAdapter.setGenerateDdl(true);
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
// factory.setPackagesToScan("com.acme.domain");
// factory.setDataSource(dataSource());
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
factory.afterPropertiesSet();
return factory.getObject();
}
@Bean
public PersistenceExceptionTranslator persistenceExceptionTranslator() {
return new EclipseLinkJpaDialect();
}
}
Database entities
com.dalarian.db.entity.help.HelpElement
package com.dalarian.db.entity.help;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.TableGenerator;
@MappedSuperclass
public abstract class HelpElement implements Comparable<HelpElement> {
@Id
@GeneratedValue(generator="HelpElement")
@TableGenerator(name="HelpElement", table="sequence",
pkColumnName="Name", pkColumnValue="HelpElement",
valueColumnName="Value", allocationSize=1)
private long id;
@Column(name="ParentID")
private long parentId;
@Column(name="Position")
private int position;
private transient Set<HelpElement> children;
@Column(name="Title")
private String title;
public boolean isRoot() {
return parentId < 1;
}
@Override
public int compareTo(HelpElement o) {
if (this.getParentId() != o.getParentId()) {
throw new UnsupportedOperationException("Cannot compare two help elements belonging to two different parents");
}
int c = new Integer(this.getPosition()).compareTo(new Integer(o.getPosition()));
if (c != 0) {
return c;
} else {
return this.getTitle().compareTo(o.getTitle());
}
}
// Getters and setters
// ...
}
com.dalarian.db.entity.help.HelpTopic
package com.dalarian.db.entity.help;
import javax.persistence.AttributeOverride;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name="help_topic")
@AttributeOverride(name="id", column=@Column(name="HelpTopicID"))
public class HelpTopic extends HelpElement {
}
com.dalarian.db.entity.help.HelpPage
package com.dalarian.db.entity.help;
import javax.persistence.AttributeOverride;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name="help_page")
@AttributeOverride(name="id", column=@Column(name="HelpPageID"))
public class HelpPage extends HelpElement {
@Column(name="Content")
private String contents;
public String getContents() {
return contents;
}
public void setContents(String contents) {
this.contents = contents;
}
}
Repositories
com.dalarian.db.repository.HelpRepository
package com.dalarian.db.repository;
import com.dalarian.db.entity.help.HelpElement;
import org.springframework.data.repository.CrudRepository;
public interface HelpRepository extends CrudRepository<HelpElement, Long>, HelpRepositoryCustom {
}
com.dalarian.db.repository.HelpRepositoryCustom
package com.dalarian.db.repository;
import com.dalarian.db.entity.help.HelpElement;
import java.util.Set;
public interface HelpRepositoryCustom {
public Set<HelpElement> findRootElements();
}
com.dalarian.db.repository.HelpRepositoryImpl
import com.dalarian.db.entity.help.HelpElement;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Set;
import java.util.TreeSet;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Repository
public class HelpRepositoryImpl implements HelpRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
private SimpleJpaRepository<HelpElement, Long> helpRepository;
@Override
public Set<HelpElement> findRootElements() {
Set<HelpElement> elements = new TreeSet<HelpElement>();
for (HelpElement element : helpRepository.findAll()) {
if (element.getParentId() == 0) {
elements.add(element);
}
}
return elements;
}
@PostConstruct
public void init() {
helpRepository = new SimpleJpaRepository<HelpElement, Long>(HelpElement.class, entityManager);
}
}
Test class
Finally, this is the code I use to test my code using JUnit:
package com.dalarian.test.audit;
import com.dalarian.ApplicationContext;
import com.dalarian.db.entity.help.HelpElement;
import com.dalarian.db.repository.HelpRepository;
import org.apache.log4j.Logger;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public final class HelpAudit {
private static final Logger LOG = Logger.getLogger(HelpAudit.class);
private HelpAudit() {}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationContext.class);
HelpRepository helpRepository = applicationContext.getBean(HelpRepository.class);
// HelpManager helpManager = applicationContext.getBean(HelpManager.class);
for (HelpElement element : helpRepository.findRootElements()) {
recursiveDisplay(element, "");
}
}
private static void recursiveDisplay(HelpElement element, String prefix) {
LOG.debug(prefix + element.getPosition() + ". " + element.getTitle());
if (element.getChildren() != null) {
for (HelpElement child : element.getChildren()) {
recursiveDisplay(child, prefix + "\t");
}
}
}
}
Resulting exception
[EL Info]: 2012-11-18 18:23:09.215--ServerSession(51898531)--Thread(Thread[main,5,main])--EclipseLink, version: Eclipse Persistence Services - 2.4.0.v20120608-r11652
[EL Fine]: connection: 2012-11-18 18:23:09.441--Thread(Thread[main,5,main])--Detected database platform: org.eclipse.persistence.platform.database.MySQLPlatform
[EL Config]: connection: 2012-11-18 18:23:09.455--ServerSession(51898531)--Connection(1856697764)--Thread(Thread[main,5,main])--connecting(DatabaseLogin(
platform=>MySQLPlatform
user name=> "***"
datasource URL=> "jdbc:mysql://localhost:3306/***"
))
[EL Config]: connection: 2012-11-18 18:23:09.466--ServerSession(51898531)--Connection(1575554065)--Thread(Thread[main,5,main])--Connected: jdbc:mysql://localhost:3306/***
User: ***@***
Database: MySQL Version: 5.1.42-community
Driver: MySQL-AB JDBC Driver Version: mysql-connector-java-5.1.12 ( Revision: ${bzr.revision-id} )
[EL Info]: connection: 2012-11-18 18:23:09.555--ServerSession(51898531)--Thread(Thread[main,5,main])--file:/***/target/classes/_default login successful
Exception in thread "main" org.springframework.dao.InvalidDataAccessApiUsageException: No [EntityType] was found for the key class [com.dalarian.db.entity.help.HelpElement] in the Metamodel - please verify that the [Entity] class was referenced in persistence.xml using a specific <class>com.dalarian.db.entity.help.HelpElement</class> property or a global <exclude-unlisted-classes>false</exclude-unlisted-classes> element.; nested exception is java.lang.IllegalArgumentException: No [EntityType] was found for the key class [com.dalarian.db.entity.help.HelpElement] in the Metamodel - please verify that the [Entity] class was referenced in persistence.xml using a specific <class>com.dalarian.db.entity.help.HelpElement</class> property or a global <exclude-unlisted-classes>false</exclude-unlisted-classes> element.
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:301)
at org.springframework.orm.jpa.DefaultJpaDialect.translateExceptionIfPossible(DefaultJpaDialect.java:120)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy16.findRootElements(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:334)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:309)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:91)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy19.findRootElements(Unknown Source)
at com.dalarian.test.audit.HelpAudit.main(HelpAudit.java:22)
Caused by: java.lang.IllegalArgumentException: No [EntityType] was found for the key class [com.dalarian.db.entity.help.HelpElement] in the Metamodel - please verify that the [Entity] class was referenced in persistence.xml using a specific <class>com.dalarian.db.entity.help.HelpElement</class> property or a global <exclude-unlisted-classes>false</exclude-unlisted-classes> element.
at org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl.entityEmbeddableManagedTypeNotFound(MetamodelImpl.java:174)
at org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl.entity(MetamodelImpl.java:194)
at org.eclipse.persistence.internal.jpa.querydef.AbstractQueryImpl.from(AbstractQueryImpl.java:97)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.applySpecificationToCriteria(SimpleJpaRepository.java:474)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.getQuery(SimpleJpaRepository.java:437)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll(SimpleJpaRepository.java:239)
at com.dalarian.db.repository.HelpRepositoryImpl.findRootElements(HelpRepositoryImpl.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:319)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
... 21 more
Sorry for the long post, but if anybody has an idea on how to fix that, I'd be glad!