I have this entity
#Entity
public class ContactList extends Base {
private static final long serialVersionUID = BaseEntity.serialVersionUID;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Collection<User> contacts = new HashSet<User>();
public ContactList() {
}
public Collection<User> getContacts() {
return contacts;
}
public void setContacts(Collection<User> contacts) {
this.contacts = contacts;
}
}
and whatever method I call from the Spring Data repository, like findAll(), I get this error
java.lang.NullPointerException: null
at org.datanucleus.store.rdbms.RDBMSStoreManager.assertCompatibleFieldType(RDBMSStoreManager.java:1005) ~[datanucleus-rdbms-4.1.1.jar:na]
at org.datanucleus.store.rdbms.RDBMSStoreManager.getBackingStoreForField(RDBMSStoreManager.java:965) ~[datanucleus-rdbms-4.1.1.jar:na]
at org.datanucleus.store.rdbms.query.BulkFetchExistsHelper.getSQLStatementForContainerField(BulkFetchExistsHelper.java:93) ~[datanucleus-rdbms-4.1.1.jar:na]
at org.datanucleus.store.rdbms.query.JPQLQuery.compileQueryFull(JPQLQuery.java:894) ~[datanucleus-rdbms-4.1.1.jar:na]
at org.datanucleus.store.rdbms.query.JPQLQuery.compileInternal(JPQLQuery.java:296) ~[datanucleus-rdbms-4.1.1.jar:na]
at org.datanucleus.store.query.Query.executeQuery(Query.java:1801) ~[datanucleus-core-4.1.1.jar:na]
at org.datanucleus.store.query.Query.executeWithMap(Query.java:1747) ~[datanucleus-core-4.1.1.jar:na]
at org.datanucleus.api.jpa.JPAQuery.getResultList(JPAQuery.java:197) ~[datanucleus-api-jpa-4.1.1.jar:na]
at org.springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:77) ~[spring-data-jpa-1.3.0.RELEASE.jar:na]
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:55) ~[spring-data-jpa-1.3.0.RELEASE.jar:na]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:95) ~[spring-data-jpa-1.3.0.RELEASE.jar:na]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:85) ~[spring-data-jpa-1.3.0.RELEASE.jar:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:312) ~[spring-data-commons-1.5.0.RELEASE.jar:na]
Any idea why?
I also tried adding targetEntity = User.class to #ManyToMany.
I'm using DataNucleus 1.4.1.
I found in the log the following lines that could be relevant:
DataNucleus.Datastore.Schema - DEBUG: Field [ContactList.contacts] -> Column(s) [contactlist.contacts] using mapping of type "org.datanucleus.store.rdbms.mapping.java.TypeConverterMapping" (org.datanucleus.store.rdbms.mapping.datastore.VarCharRDBMSMapping)
...
DataNucleus.Persistence - WARN : Member ContactList.contacts in table=contactlist has mapping=org.datanucleus.store.rdbms.mapping.java.TypeConverterMapping#6296ccf7 but expected mapping type=class org.datanucleus.store.rdbms.mapping.java.CollectionMapping
The problem was that a Converter was automatically applied. But I think this is an issue with DataNucleus, because that converter was declared something like this
public class ListOfMyEnumsToStringAttributeConverter
extends CollectionOfEnumsToStringAttributeConverter<MyEnum, List<MyEnum>>
implements AttributeConverter<List<MyEnum>, String>
Another issue with DataNucleus is that NPE is thrown even though a warning/error message should be logged.
And last, but not least, when a converter is automatically applied and this leads to an error, the converter class should be part of the log message.
Related
I experienced poor performance when using em.find(entity, primaryKey).
The reason seems to be that em.find() will also load entity collections, that are annotated with FetchType.LAZY.
This small test case illustrates what I mean:
public class OriginEntityTest4 {
[..]
#Test
public void test() throws Exception {
final OriginEntity oe = new OriginEntity("o");
final ReferencePeakEntity rpe = new ReferencePeakEntity();
oe.getReferencePeaks().add(rpe);
DatabaseAccess.onEntityManager(em -> {
em.persist(oe);
em.persist(rpe);
});
System.out.println(rpe.getEntityId());
DatabaseAccess.onEntityManager(em -> {
em.find(OriginEntity.class, oe.getEntityId());
});
}
}
#Access(AccessType.PROPERTY)
#Entity(name = "Origin")
public class OriginEntity extends NamedEntity {
[..]
private final ListProperty<ReferencePeakEntity> referencePeaks =
referencePeaks =
new SimpleListProperty<>(FXCollections.observableArrayList(ReferencePeakEntity.extractor()));
#Override
#OneToMany(mappedBy = "origin", fetch = FetchType.LAZY)
public final List<ReferencePeakEntity> getReferencePeaks() {
return this.referencePeaksProperty().get();
}
public final void setReferencePeaks(final List<ReferencePeakEntity> referencePeaks) {
this.referencePeaksProperty().setAll(referencePeaks);
}
}
I cannot find any documentation on that, my question is basically how can I prevent the EntityManager from loading the lazy collection?
Why I need em.find()?
I use the following method to decide whether I need to persist a new entity or update an existing one.
public static void mergeOrPersistWithinTransaction(final EntityManager em, final XIdentEntity entity) {
final XIdentEntity dbEntity = em.find(XIdentEntity.class, entity.getEntityId());
if (dbEntity == null) {
em.persist(entity);
} else {
em.merge(entity);
}
}
Note that OriginEntity is a JavaFX bean, where getter and setter delegate to a ListProperty.
Because FetchType.LAZY is only a hint. Depending on the implementation and how you configured your entity it will be able to do it or not.
Not an answer to titles question but maybe to your problem.
You can use also em.getReference(entityClass, primaryKey) in this case. It should be more efficient in your case since it just gets a reference to possibly existing entity.
See When to use EntityManager.find() vs EntityManager.getReference()
On the other hand i think your check is perhaps not needed. You could just persist or merge without check?
See JPA EntityManager: Why use persist() over merge()?
I have a simple project with the classes below defined. It works just fine in spring-boot 1.5.4, spring-data-commons 1.13, and spring-data-jpa 1.11.
When I upgrade to spring-boot 2.0.0.M5, spring-data-commons 2.0.0 and spring-data-jpa-2.0.0, I get a PropertyReferenceException at startup that says "No property delete found for type SimpleEntity!" Unfortunately, I can't get the stack trace out of
the computer I get the error in, it is very locked down for security.
Any ideas? Other posts I found don't seem to match my situation.
Here are the classes (altered the names, but you get the idea):
package entity;
#MappedSuperclass
public abstract class BaseEntity implements Serializable {
....
}
package entity;
#Entity
#Table(schema = "ENTITIES", name = "SIMPLE")
public class SimpleEntity extends BaseEntity {
#Column(name = "ID")
private Long id;
#Column(name = "CODE")
private String code;
#Column(name = "NAME")
private String name;
... getters and setters ...
}
package repository;
imoport org.springframework.data.repository.Repository
public interface SimpleRepository extends Repository<SimpleEntity, Long> {
public SimpleEntity save(SimpleEntity entity);
public List<SimpleEntity> save(List<SimpleEntity> entities);
public void delete(Long id);
public SimpleEntity findOne(Long id);
public List<SimpleEntity> findAllByOrderByNameAsc();
public List<SimpleEntity> findByCode(String code);
public List<SimpleEntity> findByNameIgnoreCaseOrderByNameAsc(String name);
}
Turns out there is a breaking change in Spring Data 2.0 CrudRepository interface. The error I received occurs under the following conditions:
You have a 1.x Sping Data project
You have an interface that extends Repository directly, not a subinterface like CrudRepository
Your Repository subinterface declares the "void delete(ID)" method found in CrudRepository (in my case "void delete(Long)"
You update to Spring Data 2.x
The problem is that CrudRepository in 2.x no longer has a "void delete(ID)" method, it was removed, and a new method "void deleteById(ID)" was added.
When Spring data sees a delete method signature it doesn't recognize, it produces an error about your entity class missing a delete property - this is true of both 1.2 and 2.x.
Annotating the Spring Data JPA repository method findAll() with #EntityGraph:
import org.springframework.data.jpa.repository.JpaRepository;
[...]
public interface OptgrpRepository extends JpaRepository<Optgrp> {
#EntityGraph(value = "Optgrp.sysoptions")
List<Optgrp> findAll();
}
leads to this error message:
org.springframework.data.mapping.PropertyReferenceException: No property findAll found for type Optgrp!
Same error happens when changing findAll() to other names:
findAllWithDetail() --> No property findAllWithDetail found for type Optgrp!
findWithDetailAll() --> No property findWithDetailAll found for type Optgrp!
Question: Is it at all possible to use the #EntityGraph annotation on a Spring Data JPA repository method that finds all entities?
EDIT: as asked in the comment, here's the extract from the Optgrp entity class:
#Entity
#NamedEntityGraph(name = "Optgrp.sysoptions", attributeNodes = #NamedAttributeNode("sysoptions"))
public class Optgrp implements Serializable {
[...]
#OneToMany(mappedBy="optgrp", cascade = CascadeType.ALL, orphanRemoval=true)
#OrderBy(clause = "ordnr ASC")
private List<Sysoption> sysoptions = new ArrayList<>();
}
And the Sysoption entity class as well:
#Entity
public class Sysoption implements Serializable {
[...]
#ManyToOne
#JoinColumn(name = "optgrp_id", insertable=false, updatable=false)
private Optgrp optgrp;
}
For all, who are using Stack Overflow as knowledge database too, I record a new status to Markus Pscheidts challenge. Three years and six months later the #EntityGraph annotation works now directly at the findAll() function in Spring Data JpaRepository, as Markus original expected.
#Repository
public interface ImportMovieDAO extends JpaRepository<ImportMovie, Long> {
#NotNull
#Override
#EntityGraph(value = "graph.ImportMovie.videoPaths")
List<ImportMovie> findAll();
}
Versions used in the test: Spring Boot 2.0.3.RELEASE with included spring-boot-starter-data-jpa.
Using the name findByIdNotNull is one way to combine both findAll() and entity graph:
#EntityGraph(value = "Optgrp.sysoptions")
List<Optgrp> findByIdNotNull();
I'm using EclipseLink and I'd like to check whether entity and table definitions are consistent.
I found "Integrity Checker" and tried it.
public final class EMF {
public static class EnableIntegrityChecker implements SessionCustomizer {
#Override
public void customize(Session session) throws Exception {
session.getIntegrityChecker().checkDatabase();
session.getIntegrityChecker().setShouldCatchExceptions(false);
}
}
private static final EntityManagerFactory INSTANCE;
static {
String appId = SystemProperty.applicationId.get();
Map<String, String> overWriteParam = new HashMap<>();
overWriteParam.put(
PersistenceUnitProperties.SESSION_CUSTOMIZER,
EnableIntegrityChecker.class.getName());
INSTANCE = Persistence.createEntityManagerFactory("unit", overWriteParam);
}
private EMF() {
}
public static EntityManager create() {
return INSTANCE.createEntityManager();
}
}
Some cases it can detect inconsistency, but some cases can not.
If entity has variable A and table does not have column A, Integrity Checker can found inconsistency.
If table has colume A and entity does not have variable A, Integrity Checker can not found inconsistency.
If column A in table is int and variable A in entity is String, Integrity Checker can not found inconsistency.
How can I detect inconsistency in case 2 and 3?
You can extend IntegrityChecker, override it's checkTable method and use it via session.setIntegrityChecker(customIntegrityChecker). Note that some of validations are located in ClassDecriptor#checkDatabase so it's hard to directly re-use them and properly report exact error cause.
I am using Spring Data MongodB 1.4.2.Release version. For Spring Data MongoDB, I have created the custom repository interface and implementation in one location and create custom query function getUsersName(Users users).
However I am still getting below exception:
Caused by: org.springframework.data.mapping.PropertyReferenceException:
No property get found for type Users! at org.springframework.data.mapping.PropertyPath. (PropertyPath.java:75) at
org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:327) at
org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:359) at
org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:359) at
org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:307) at
org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:270) at
org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:241) at
org.springframework.data.repository.query.parser.Part.(Part.java:76) at
org.springframework.data.repository.query.parser.PartTree$OrPart.(PartTree.java:201) at
org.springframework.data.repository.query.parser.PartTree$Predicate.buildTree(PartTree.java:291) at
org.springframework.data.repository.query.parser.PartTree$Predicate.(PartTree.java:271) at
org.springframework.data.repository.query.parser.PartTree.(PartTree.java:80) at
org.springframework.data.mongodb.repository.query.PartTreeMongoQuery.(PartTreeMongoQuery.java:47)
Below is my Spring Data MongoDB structure:
/* Users Domain Object */
#Document(collection = "users")
public class Users {
#Id
private ObjectId id;
#Field ("last_name")
private String last_name;
#Field ("first_name")
private String first_name;
public String getLast_name() {
return last_name;
}
public void setLast_name(String last_name) {
this.last_name = last_name;
}
public String getFirst_name() {
return first_name;
}
public void setFirst_name(String first_name) {
this.first_name = first_name;
}
}
/* UsersRepository.java main interface */
#Repository
public interface UsersRepository extends MongoRepository<Users,String>, UsersRepositoryCustom {
List findUsersById(String id);
}
/* UsersRepositoryCustom.java custom interface */
#Repository
public interface UsersRepositoryCustom {
List<Users> getUsersName(Users users);
}
/* UsersRepositoryImpl.java custom interface implementation */
#Component
public class UsersRepositoryImpl implements UsersRepositoryCustom {
#Autowired
MongoOperations mongoOperations;
#Override
public List<Users> getUsersName(Users users) {
return mongoOperations.find(
Query.query(Criteria.where("first_name").is(users.getFirst_name()).and("last_name").is(users.getLast_name())), Users.class);
}
/* Mongo Test function inside Spring JUnit Test class calling custom function with main UsersRepository interface */
#Autowired
private UsersRepository usersRepository;
#Test
public void getUsersName() {
Users users = new Users();
users.setFirst_name("James");`enter code here`
users.setLast_name("Oliver");
List<Users> usersDetails = usersRepository.getUsersName(users);
System.out.println("users List" + usersDetails.size());
Assert.assertTrue(usersDetails.size() > 0);
}
The query method declaration in your repository interface is invalid. As clearly stated in the reference documentation, query methods need to start with get…By, read_By, find…By or query…by.
With custom repositories, there shouldn't be a need for method naming conventions as Oliver stated. I have mine working with a method named updateMessageCount
Having said that, I can't see the problem with the code provided here.
I resolved this issue with the help of this post here, where I wasn't naming my Impl class correctly :
No property found for type error when try to create custom repository with Spring Data JPA