Problem saving Neo4j 1:many #Relationship with polymorphic set - java

When saving the Dater class below with Neo4jRepository::save (spring-data-neo4j-6.1.5) the save hangs and never returns. I believe it has something to do with my Dater object having a relationship defined by a set referencing an interface instead of a class with a #Node annotation. Is this an issue for neo4j?
//PersistentDaterMusicItem is interface. Is there a problem doing this?
#Relationship(type = "LISTENS_TO_MUSIC")
private Set<PersistentDaterMusicItem> musicItems = new HashSet<>();
//parent
#Node
public class Dater{
#Id
#GeneratedValue
//set of different implementations of PersistentDaterMusicItem
#Relationship(type = "LISTENS_TO_MUSIC")
private Set<PersistentDaterMusicItem> musicItems = new HashSet<>();
}
//inteface 1
public interface PersistentLibraryMusicItem extends PersistentDaterMusicItem{
LocalDateTime getAddedDateTime();
}
//interface 2
public interface PersistentListenedMusicItem extends PersistentDaterMusicItem{
LocalDateTime getListenedDateTime();
}
//impl 1 of PersistentDaterMusicItem
#Node
public class ListenedAppleSong extends AppleSong implements PersistentListenedMusicItem{
#Id
#GeneratedValue
private final Long id;
}
//impl 2 of PersistentDaterMusicItem
#Node
public class LibraryAppleSong extends AppleSong implements PersistentLibraryMusicItem{
#Id
#GeneratedValue
private final Long id;
}

It turns out the problem was unrelated to what I thought. My constructor to ListenedAppleSong took an argument that was not a field of the class. Since Spring data expects constructor args to exist as fields it was failing with error: "Required property appleSong not found for class com.dapp.common.model.apple.ListenedAppleSong!" Solution was to make sure all constructor args are fields in the class. Unfortunately this error was not thrown during execution and the code just hung so it took a while to debug.

Related

JPA how to annotate generic entity field conditionally?

I have this generic entity:
#MappedSuperclass
abstract class Position<T> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Enumerated(EnumType.STRING)
private T name;
}
But there's a case where the generic type is a String:
#Entity
class ChildPosition0 extends Position<String> {
}
And, JPA will complaint that String is not an enum in this case, but I need to annotate this name field if it's an enum, if not, the database will mark it as int type, and that's not ideal. How do I solve this? How to annotate the field conditionally?
My workaround:
Use Position as a parent class, and adding those field in child class individually, even though they share the same field:
#MappedSuperclass
abstract class Position {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
}
And extends it from child entity like this:
Child1:
#Entity
public class ChildPosition1 extends Position {
#Enumerated(EnumType.STRING)
private Priority name; // <- Priority is enum type
}
Child2:
#Entity
public class ChildPosition2 extends Position {
private String name;
}
This is too ugly IMO. And Java does not allow class field override from child class. So, back to the question: how to annotate generic field conditionally?

Abstract class inheritance in springboot

I am a bit confused in regards to abstract classes and help would be great.
I have a the following classes
public abstract class AbstractUser{
private String username;
private String password;
}
And then I have this class
#Entity
public class Company extends AbstractUser{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String company_name
}
Now when I launch the application and I check the h2-console, the table Company only has the id and company_name. not the abstract classes variables. Is there a way to make it so it gets all the variables?
thank you in advance
You can use #MappedSuperclass Jpa annotation on the AbstractUser, Also consider moving the id attribute to the abstract class.
#MappedSuperclass
public abstract class AbstractUser{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
}
To resolve this, you will need JPA annotation on your abstract class. This is not specific to Spring Boot, it's JPA thing.
You can apply #MappedSuperclass on your abstract class and that should resolve this issue. Find More: Inherit Super Class Properties

How to inherit from multiple base abstract classes in JPA

I faced a problem how I can create JPA entity which extends multiple base abstract classes (without creating additional table). I know there is an annotation #MappedSuperclass, but it gives an ability to create only one base class as soon as we use extends and multiple inheritance is not a Java feature.
For example, I have two bases:
#MappedSuperclass
public abstract class Authored {
#ManyToOne
private User user;
}
and
#MappedSuperclass
public abstract class Dated {
private String creationDate;
}
I expect that some of my models will extend only Authored, some -- only Dated, and some -- both.
Though it's only possible to write
#Entity
public class MyEntity extends Authored {
...
}
or
#Entity
public class MyEntity extends Dated {
...
}
Some discussions propose to inherit classes in line (e.g. Authored and AuthoredAndDated) but this seems too dirty, none of this bases logically can't extend another one.
ADDITION
I have to note that this style is supported in other frameworks like Django (due to multiple inheritance in python) so it's strange that JPA doesn't support it.
I am sorry to disappoint you, but there is no other solution than creating AuthoredAndDated as you suggested.
We faced in the same issue for our entities and went with the same procedure.
We have a
#MappedSuperclass
public class TimestampedObject {
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_at")
private Date createdAt;
#UpdateTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "updated_at")
private Date updatedAt;
}
and a
#MappedSuperclass
public class IdObject {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id", updatable = false, columnDefinition = "serial")
private Long id;
}
Thus we created a TimestampedIdObject for this purpose.
Edit:
If you find another suitable solution, it would be great if you could post it here, as we have the same issue...
You should use an #Embeddable / #Embedded for goal by replacing inheritance with composition.
First, do not use #MappedSuperClass, but use #Embeddable instead for your classes you want to share the attributes with:
#Embeddable
public class Authored {...}
#Embeddable
public class Dated {...}
In the next step your Entity should not inherit from Authored or Dated but instead get an attribute referencing them:
#Entity
public class MyEntity {
#Embedded
private Authored authored;
#Embedded
private Dated dated;
}
If you want to get behaviour out of this, where you can generically access without those new attributes, you would need to introduce an interface exposing the necessary methods.
For expample if MyEntity should be able to provide details on last updates and creation, you would introduce an interface Authorable which defines to methods to access the relevant data.
public interface Authorable { /* necessary methods */ }
MyEntity will implement this interface then:
#Entity
public class MyEntity implements Authorable {
/* previous content plus implemented mehtods from interface */
}

Jpa 2.0 - EntityManager.find (SomeEntity.class,PK) need to fill Descriminator value to key

I have a problem, I have two entity Job and JobPK
Job class looks like this sample code :
#Entity
#IdClass(JobPK.class)
#Table(name="JOB")
#Inheritance
#DiscriminatorColumn(name="JOB_TYPE")
public abstract class Job implements Serializable {
#Id
#Column(name="FOLDER_ID")
private BigDecimal folderId;
#Id
#ColumnDefinition(position = 1)
private String name;
#Column(name="JOB_TYPE",insertable=false,updatable=false)
private String jobType;
...
}
and JobPk :
public class JobPK implements Serializable {
private static final long serialVersionUID = -3266336718203527905L;
#Column(name="JOB_TYPE",insertable=false,updatable=false)
private String jobType;
#Id
private String name;
#Id
#Column(name="FOLDER_ID")
private BigDecimal folderId;
......
}
I have two class which extends Job : CalculatingJob and ImportingJob
Now I wont to use :
getEntityManager().find(CalculatingJob.class, new JobPK (BigDecimal.valueOf(folderId),name))
and I have problem because I must fill i JobPK descriminator value field. If I don't do that I've got Null Pointer Exception. Descriminator value is in key by default I think but I don't want put information about descriminator value explicite during JobPk creating. I thought that Entity which extends from Job will fill this field automaticaly. Any Idea to bypass this problem, maybe I can get Annotation #DescriminatorVale from CalculatingJob and then put into constructor JobPk
Thanks for Help
Try this configuration for Hierarchy structure
Job.java
#Table(name = "JOB")
#Inheritance
#IdClass(JobPK.class)
#DiscriminatorColumn(name = "JOB_TYPE", discriminatorType = DiscriminatorType.STRING)
public abstract class Job implements java.io.Serializable {
}
CalculatingJob.java
#Entity
#DiscriminatorValue("CalculatingJob")
public class CalculatingJob extends Job {
}
ImportingJob.java
#Entity
#DiscriminatorValue("ImportingJob")
public class ImportingJob extends Job {
}
JobPK.java
public class JobPK implements Serializable {
}
The discriminator value is entered by hibernate.

How to call #NamedQueries from interface method definition?

I'm very new in Spring Framework, I want to know if is possible invoke Entity Named Query only defining the Named Query on the interface without any implementation.
I want to do something like this.
NamedQuery(name = "StateBo.findByCountry", query = "SELECT state FROM StateBo state WHERE state.country.id = ?")
#Table(name = "STATE")
#Entity(name = "StateBo")
public class StateBo extends BaseNamedBo {
private static final long serialVersionUID = 3687061742742506831L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "STATE_ID")
private Long id;
#Column(name = "ISO_CODE")
private String isoCode;
#ManyToOne
#JoinColumn(name = "COUNTRY_ID")
private CountryBo country;
// getters and setters ...
}
I defined the Named Query StateBo.findByBCountry, my interface looks like this
public interface IStateDao extends JpaRepository<StateBo, Long> {
public List<StateBo> findByCountry(Long id);
}
And the interface implementation looks like this.
#Transactional
#Repository("stateDao")
public class StateDao implements IStateDao {
}
But I have the error that I have to implement the methods that I'm defining on my interface, but I don't want to do that. I only want define my Named Query and define the method in my interface with the same name that is on the Entity and don't add the implementation of that method because the implementation is basically the String Named Query
You can use Spring Data Jpa project.
For start you see https://spring.io/guides/gs/accessing-data-jpa/
To execute query without an implementation(only interface) see http://docs.spring.io/spring-data/jpa/docs/1.6.0.RELEASE/reference/htmlsingle/#jpa.query-methods.at-query
Basically you don't need the implementation:
#Transactional
#Repository("stateDao")
public class StateDao implements IStateDao {
}
try to remove that and see what will happen.

Categories