I am trying to understand the annotations MapKey and MapKeyColumn and I have found them confusing. I was reading an article that made me even more confused (The specification section)
I have an entity with an int field and it is not the primary key:
public class Connections{
...
public final int getConnectionId() {
return this.connectionId;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FK_StartpointTNA")
public final Endpoint getStartpoint() {
return this.startpoint;
}
...
}
and in the other side I have
public class Endpoint{
...
#OneToMany(mappedBy = "startpoint", fetch = FetchType.LAZY, cascade = { javax.persistence.CascadeType.REMOVE })
#MapKeyColumn(name = "connectionId")
public Map<Integer, Connections> getConnections() {
return this.connections;
}
....
}
I dont know really how to fix this. I keep getting: org.apache.openjpa.persistence.ArgumentException: "connections" declared that it is mapped by "startpoint", but that is a not a field of the related type.
what is the proper way to map this?
As someone posted to the JIRA you opened, get rid of the final on your methods.
From the JPA 2 spec:
(Section 2.1 "The Entity Class", page 21) states: "The entity class must not be final. No methods or persistent instance variables of the entity class may be final."
I had the same issue and getting the same error-message
but in my case I was wrong with
mappedBy = "foo"
where foo must declare the field! Not the column.
Related
I am using Spring Data Neo4j RX. And I have a query like this:
#Query("MATCH (a:Repo)-[:REPO_DEPEND_ON]->(b:Repo) WHERE a.name= $name RETURN a.name, b.name")
String[] getSingleRepoDependencyTo(String name);
I know the return type is wrong here, as it cannot be a String array. But how can I get the result properly, which contains two fields?
I have searched online for a long time but cannot find an answer. The "#QueryResult" annotation is not supported in this RX version yet.
Thanks for your help.
Assuming that you have a mapped #Node Repo with its relationships like
#Node
public class Repo {
// other things
String name;
#Relationship("REPO_DEPEND_ON") Repo repo;
}
and defining this method in a ...extends Neo4jRepository<Repo,...>,
you could use Projections.
public interface RepoProjection {
String getName();
DependingRepo getRepo();
/**
* nested projection
*/
interface DependingRepo {
String getName();
}
}
Important to keep in mind that the returned values should be the nodes and relationship to make it work this way.
You could also remove the custom query and do something like:
RepoProjection findByName(String name)
if you do not have the need for a findByName in this repository for the entity itself.
Take a look here: https://neo4j.github.io/sdn-rx/current/#projections.interfaces
It seems to list exactly what you want. From those docs:
interface NamesOnly {
String getFirstName();
String getLastName();
}
interface PersonRepository extends Neo4jRepository<Person, Long> {
List<NamesOnly> findByFirstName(String firstName);
}
There are some other variations too.
You can use annotation #QueryResult on your expected model. For instance you can do that in this way.
DTO:
import org.springframework.data.neo4j.annotation.QueryResult;
#QueryResult
public class SomeDto {
private int someInt;
private SomeObject sobj;
private double sdouble;
private AnotherObject anObj;
//getters setters
}
Neo4jRepository:
public interface DomainObjectRepository extends Neo4jRepository<DomainObject, Long> {
#Query("MATCH(n:SomeTable) RETURN someInt, sobj, sdouble, anObj") //Return a few columns
Optional<SomeDto> getSomeDto();
}
At my Neo4j/SDN 4 project I have a following entity:
#NodeEntity
public class Value extends BaseEntity {
#Index(unique = false)
private Object value;
private String description;
...
}
During the application run-time I want to be able to add a new dynamic properties to Value node, like for example value_en_US, value_fr_FR.
Right now I don't know what exact properties will be added to a particular Value node during application run-time so I can't define these properties at the code as a separate fields in Value.
Is there at SDN 4 any mechanisms to define these properties during the application run-time? I need something similar to DynamicProperties from SDN 3.
There is no such functionality in SDN 4, but it will be added in SDN 5 through a #Properties annotation on Map.
It will be available for testing in snapshot version very soon.
Check out this commit for more details
You might also want to look at this response to a similar question.
https://stackoverflow.com/a/42632709/5249743
Just beware that in that answer the function:
public void addAllFields(Class<?> type) {
for (Field field : type.getDeclaredFields()) {
blacklist.add(field.getName());
}
if (type.getSuperclass() != null) {
addAllFields(type.getSuperclass());
}
}
is not bullet proof. For one thing it doesn't look at #Property annotations. So if you want to go down that route keep your eyes open.
An 'improvement' is
public void addAllFields(Class<?> type) {
for (Field field : type.getDeclaredFields()) {
blacklist.add(findName(field));
}
if (type.getSuperclass() != null) {
addAllFields(type.getSuperclass());
}
}
private String findName(Field field) {
Property property = field.getAnnotation(Property.class);
if(property == null || "".equals(property.name())) {
return field.getName();
} else {
return property.name();
}
}
But this obviously doesn't look for the annotation on methods...
I'm having a problem querying based on an Enum property of my NodeEntity.
The NodeEntity in question is defined:
#NodeEntity(label = "Entity")
public class MyEntity {
#GraphId
private Long internalId;
....
private State state;
#Transient
public enum State {
STATEONE, STATETWO, STATETHREE
}
....
It saves without a problem, the state Enum represented perfectly, and I can query using other properties (Strings) with no problem at all. However the problem is the following query in a repository:
#Query("MATCH (entity:Entity {state:{0}})" +
"RETURN entity")
List<MyEntity> findByState(MyEntity.State state)
i.e. find all entities with the given state.
There's no exception, however using this simply returns a List of 0 Entities.
I've tried all kinds of variations on this, using a WHERE clause for example, with no luck.
The Entities are persisted properly, using findAll() in the same test returns the expected List of Entities with their states exactly as I would expect.
Any thoughts?
Not quite sure what the value #Transient adds to the enum. It is anyway not persistable as a node or relationship in Neo4j. It is sufficient to define the field as one that should persist with
private State state;
and leave off the #Transient annotation from the enum.
With it, SDN ignores the field sent to the derived query.
However, if you have a good reason to mark the enum #Transient, please do share it and we'll re-visit this case.
There is a general problems using spring data rest interface to search on enum fields. Just using the enum-to-string converter cannot work for search where you want to find if the value is IN a collection of values:
public interface AppointmentRepository extends Neo4jRepository<Appointment, Long> {
Page<Appointment> findByDayOfWeekIn(#Param("days") List<DayOfWeek> days, Pageable pageable);
}
The above does not work out of the box because neo4j will try to convert a List to your property type: DayOfWeek
In order to work around this I needed a custom converter that handles both requests providing collection of values (the search) and single values (the normal read and write entity):
#SuppressWarnings({ "unchecked", "rawtypes" })
public abstract class SearchQueryEnumConverter<T extends Enum> {
private Class<T> enumType;
public SearchQueryEnumConverter() {
enumType = (Class<T>) ((ParameterizedType) this.getClass()).getActualTypeArguments();
}
public Object toGraphProperty(Object value) {
if (Collection.class.isAssignableFrom(value.getClass())) {
List<T> values = (List<T>) value;
return values.stream().map(Enum::name).collect(Collectors.toList());
}
return ((Enum) value).name();
}
public Object toEntityAttribute(Object value) {
if (Collection.class.isAssignableFrom(value.getClass())) {
List<String> values = (List<String>) value;
return values.stream().map(v -> (T) T.valueOf(enumType, v)).collect(Collectors.toList());
}
return (T) T.valueOf(enumType, value.toString());
}
}
The abstract converter can be reified by all enums, and used as parameter of the #Convert annotation:
public enum EnumType {
VALUE_A, VALUE_B;
public static class Converter extends SearchQueryEnumConverter<EnumType> implements AttributeConverter {
}
}
#NodeEntity
public Entity {
#Property
#Convert(EnumType.Converter.class)
EnumType type;
}
I'm having a problem where the related table id fields return 'null' from my domain objects when using inheritance. Here is an example:
In /src/groovy/
BaseClass1.groovy
class BaseClass1 {
Long id
static mapping = {
tablePerConcreteClass true
}
}
BaseClass2.groovy
class BaseClass2 extends BaseClass1 {
String someOtherProperty
static constraints = {
someOtherProperty(maxSize:200)
}
static mapping = BaseClass1.mapping
}
In /grails-app/domain
ParentClass.groovy
class ParentClass extends BaseClass2 {
ChildClass myChild
static mapping = BaseClass2.mapping << {
version false
}
}
ChildClass.groovy
class ChildClass extends BaseClass1 {
String property
static mapping = BaseClass1.mapping
}
The problem appears here:
SomeotherCode.groovy
print parentClassInstance.myChild.id // returns the value
print parentClassInstance.myChildId // returns null
Any ideas what might be going on to get those dynamic properties to break like this?
After debugging into the get(AssociationName)Id source, I found the following:
The handler for this is:
GrailsDomainConfigurationUtil.getAssociationIdentifier(Object target, String propertyName,
GrailsDomainClass referencedDomainClass) {
String getterName = GrailsClassUtils.getGetterName(propertyName);
try {
Method m = target.getClass().getMethod(getterName, EMPTY_CLASS_ARRAY);
Object value = m.invoke(target);
if (value != null && referencedDomainClass != null) {
String identifierGetter = GrailsClassUtils.getGetterName(referencedDomainClass.getIdentifier().getName());
m = value.getClass().getDeclaredMethod(identifierGetter, EMPTY_CLASS_ARRAY);
return (Serializable)m.invoke(value);
}
}
catch (NoSuchMethodException e) {
// ignore
}
catch (IllegalAccessException e) {
// ignore
}
catch (InvocationTargetException e) {
// ignore
}
return null;
}
It threw an exception on the related class (value.getClass().getDeclaredMethod), saying NoSuchMethod for the method getId(). I was unable to remove the id declaration from the base class without Grails complaining that an identifier column was required. I tried marking id as public and it also complained that it wasn't there. So, I tried this
BaseClass {
Long id
public Long getId() { return this.#id }
}
and things worked on some classes, but not on others.
When I removed the ID declaration, I go an error: "Identity property not found, but required in domain class". On a whim, I tried adding #Entity to the concrete classes and viola! everything started working.
class BaseClass {
//Don't declare id!
}
#Entity
class ParentClass {}
#Entity
class ChildClass {}
I still think it is a grails bug that it needs to be added, but at least it is easy enough to work around.
I'm not sure why you are seeing this behavior, but I'm also not sure why you are doing some of the things you are doing here. Why have a domain class extend a POGO? Domains, Controllers, and Services are heavily managed by the Grails machinery, which probably was not designed for this sort of use. Specifically, I believe Grails builds the dynamic property getters for the GrailsDomainProperty(s) of GrailsDomainClass(es), not POGO's. In this case, you have an explicitly declared id field in BaseClass1 that is not a GrailsDomainProperty. I suspect that this POGO id property is not picked up by the Grails machinery that creates the dynamic property getters for Domains.
You might try putting BaseClass1/2 in /grails-app/domain, perhaps making them abstract if you don't want them instantiated, then extending them as you are and seeing if you observe the behavior you want.
I use int? for all my required 'FK' properties in ViewModels. This gives me an easy way of specifying on a Create view model that a value is nullable and must be assigned a value to satisfy the Required attribute.
My problem comes in because I create the domain model entity first, using a domain factory, then map it to the view model. Now, many of the nullable ints in the view model get assigned 0 from non-nullable ints in the domain model. I would prefer not to build the new entity in the view model and only map it back to the domain model to avoid his. What else can I do? i'm sure there is som Automapper voodoo that can help me.
EDIT: you dont need to do any of this, but i thought i'd leave it here for people looking for a similar solution. really all you have to do is just provide a mapping from int to int? like this: Mapper.Map<int, int?>()
in that case, I believe you could use a custom type converter, which inherits from automappers ITypeConverter. This code works, I've run it through .NET Fiddle:
using System;
using AutoMapper;
public class Program
{
public void Main()
{
CreateMappings();
var vm = Mapper.Map<MyThingWithInt, MyThingWithNullInt>(new MyThingWithInt());
if (vm.intProp.HasValue)
{
Console.WriteLine("Value is not NULL!");
}
else
{
Console.WriteLine("Value is NULL!");
}
}
public void CreateMappings()
{
Mapper.CreateMap<int, int?>().ConvertUsing(new ZeroToNullIntTypeConverter ());
Mapper.CreateMap<MyThingWithInt, MyThingWithNullInt>();
}
public class ZeroToNullIntTypeConverter : ITypeConverter<int, int?>
{
public int? Convert(ResolutionContext ctx)
{
if((int)ctx.SourceValue == 0)
{
return null;
}
else
{
return (int)ctx.SourceValue;
}
}
}
public class MyThingWithInt
{
public int intProp = 0;
}
public class MyThingWithNullInt
{
public int? intProp {get;set;}
}
}
You can always use the .ForMember() method on your mapping. Something like this:
Mapper
.CreateMap<Entity, EntityDto>()
.ForMember(
dest => dest.MyNullableIntProperty,
opt => opt.MapFrom(src => 0)
);