I have two entities as such:
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "numeroOfferta")
#Entity
#Table(name=DatabaseConstants.TABELLA_OFFERTE)
public class Offerta {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private BigInteger numeroOfferta;
#ManyToOne(fetch=FetchType.EAGER)
private Cliente cliente;
private Double importoOfferta;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "offerta")
private Set<Ordine> ordini;
And:
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
#Entity
#Table(name=DatabaseConstants.TABELLA_ORDINI)
public class Ordine {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private BigInteger id;
private String numeroOrdine;
#ManyToOne(fetch=FetchType.EAGER)
private Offerta offerta;
private String stato;
#OneToOne(fetch= FetchType.EAGER)
private Binder binder;
Where exist a many-to-one relationship between the ORDINE and OFFERTA objects (more ORDINE to one OFFERTA).
When the entities are serialized in JSON it appears that if there is more than one ORDINE, only the first one is retreived completely, while the others appear only as their ID.
What I need is to serialize the ORDINE and its OFFERTA, without going back to ORDINE.
Is the use of #JsonIdentityInfo correct?
Can anyone provide an explanation of the behavior described?
Related
I have a parent entity 'contracts' that has a one-to-one relation with another entity 'child-contract'. the interesting thing is that the mapping field ('contract_number')id not a primary key-foreign key but is rather a unique field in both the tables. Also it is possible for a contracts to not have any child contract altogether. With this configuration I have observed hibernate to generate 1 additional query every time a contracts does not have a child-contract. I filed this behavior very strange. Is there a way to stop these unnecessary query generation or have I got something wrong.
below is a piece of my code configuration.
#Data
#Entity
#Table(name = "contracts")
public class Contracts implements Serializable {
#Id
#JsonIgnore
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
#OneToOne(fetch=FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JsonProperty("crm_contracts")
#JoinColumn(name = "contract_number", referencedColumnName = "contract_number")
private ChildContract childContract ;
}
#Data
#NoArgsConstructor
#Entity
#Table(name = "child_contract")
#BatchSize(size=1000)
public class ChildContract implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#JsonProperty("id")
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
}
Please help.
Thank-you
You can use NamedEntityGraph to solve multiple query problem.
#NamedEntityGraph(name = "graph.Contracts.CRMContracts", attributeNodes = {
#NamedAttributeNode(value = "crmContract") })
Use this on your repository method as
#EntityGraph(value = "graph.Contracts.CRMContracts", type = EntityGraphType.FETCH)
// Your repo method in repository
I am trying to solve JPA problem. I have 2 main entities - CameraItem and Chain (which represents ordered list of cameras)
Now there have to be 2 #ManyToMany relationships between CameraItem and Chain.
Each CameraItem has at least one parent Chain. As one CameraItem can belong to different Chains, and each Chain can have multiple CameraItems this is the first simple direct #ManyToMany relationship.
Chains can be connected with each other via CameraItem. In other words, CameraItem is holding the connection between Chains. But this is not simple #ManyToMany relationship, because we also need information about direction of the Chains connection. So it is #ManyToMany relationship with new Entity as Baeldung describes here https://www.baeldung.com/jpa-many-to-many. Entity ConnectionPoint is holding the information about the direction as a String.
I paste the classes here:
CHAIN CLASS:
#Entity
#Table(name = "chain")
public class Chain {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#NotBlank(message = "Chain name is mandatory")
private String name;
#Column(name = "PLANT_NAME")
private String plantName;
private String description;
private String status;
private Boolean hasPlant;
#CreationTimestamp
#Column(name = "creation_time")
private LocalDateTime creationTime;
#OneToMany(mappedBy = "camera_item")
private List<CameraItem> cameraItems = new ArrayList<>();
#OneToMany(mappedBy = "chain")
Set<ConnectionPoint> connectionPoints;
CAMERA ITEM CLASS:
#Entity
#Table(name = "camera_item")
public class CameraItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn
private Camera camera;
private String name;
private Integer positionInChain;
#ManyToMany(mappedBy = "cameraItems", fetch = FetchType.LAZY)
private List<Chain> parentChainIds;
#OneToMany(mappedBy = "cameraItem")
Set<ConnectionPoint> connectionPoints;
CONNECTION POINT CLASS:
#Entity
#Table(name = "connection_point")
public class ConnectionPoint {
#Id
private Long id;
#Column(name = "direction")
private String direction;
#ManyToOne
#JoinColumn(name = "chain")
private Chain chain;
#ManyToOne
#JoinColumn(name = "camera_item")
private CameraItem cameraItem;
When I run the application I get this error:
org.hibernate.AnnotationException: mappedBy reference an unknown
target entity property:
no.trafsys.videodashboard.model.entity.CameraItem.camera_item in
no.trafsys.videodashboard.model.entity.Chain.cameraItems
Does somebody know where the problem can be?
I use #OneToMany annotations in Chain and CameraItem entities and #ManyToOne in ConnectionPoint like Baeldung in his tutorial.
Thank you in advance for any help
I don't think there is issue in ConnectionPoint. I think the issue is that:
In Chain class,
#OneToMany(mappedBy = "camera_item") // One-to-Many defined here
private List<CameraItem> cameraItems = new ArrayList<>();
while in CameraItem class, corresponding property is defined as follow:
#ManyToMany(mappedBy = "cameraItems", fetch = FetchType.LAZY) // Many-To-Many
private List<Chain> parentChainIds;
Try changing the mapping type to #ManyToMany in Chain class as well. It might work.
PS: I am not entirely sure of this, but this feels like the issue[incorrect mapping type]. Wanted to add this as a comment, but due to space issues, adding it as an answer.
#Entity
#Table(name = "chain")
public class Chain {
//..
#OneToMany(mappedBy = "camera_item")
private List<CameraItem> cameraItems = new ArrayList<>();
//..
}
mappedBy parameter can only be in one side of the relation. I suspect camera_item is database table column name. So your cameraItems needs #JoinTable(name = "camera_item"... annotation
So i have a very basic construction where i have a client and this client could have multiple addresses.
So in hibernate i did something like this
#Entity
#Table(name ="tbl_clients")
#Access(value = AccessType.FIELD)
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_client")
private Integer id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "fkIdClientAddress", cascade = CascadeType.ALL)
private List<AddressClient> addressClientList = new ArrayList<>();
And the other class looks like this :
#Entity
#Table(name ="tbl_clients_address")
#Access(value = AccessType.FIELD)
public class AddressClient {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_client_address")
private Integer id;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="id_client")
private Client Client;
#Column
private Integer fkIdClientAddress;
When inserting a client into the database who has 2 addresses it works but the fields fkIdClientAddress and id_client is are empty in the database. So i have no idea to who the address belong.
How can i fix this? And what is wrong with this construction?
first improvements
Class AddressClient
#Entity
#Table(name ="tbl_clients_address")
#Access(value = AccessType.FIELD)
public class AddressClient {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_client_address")
private Integer id;
#ManyToOne
#JoinColumn(name="id_client")
private Client client;
Class Client
#Entity
#Table(name ="tbl_clients")
#Access(value = AccessType.FIELD)
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_client")
private Integer id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "client", cascade = CascadeType.ALL)
private List<AddressClient> addressClientList = new ArrayList<>();
This is looking better but the field id_client is still null
When i created a for each and save the AddressClients again the id is successful saved.
#RequestMapping(value = "/addclient",method = RequestMethod.POST)
public void addClient(#AuthenticationPrincipal Principal user,#RequestBody Client client) {
//FIND THE ACTIVE USER
LOGGER.info("SQL: GET PRINCEPAL USER");
User getuser = userDao.findByEmail(user.getName());
for (AddressClient addressClient : client.getAddressClientList())
{
addressClient.setClient(client);
}
clientDao.save(client);
}
Your mapping is wrong. First of all, you don't need two different columns (id_client and fkIdClientAddress) to know that a given address belongs to a given client. So the first thing to do is to remove fkIdClientAddress.
Then the mappedBy attribute in OneToMany is usd to tell Hibernate which field in address represents the owning ManyToOne association. So it must be set to Client. (or, if you respect the Java naming conventions and rename the field to client, it must be set to client).
Finally, having cascade=ALL on a ManyToOne doesn't make much sense: you don't want to delete the client when you delete one of its addresses. That would fail anyway, since other addresses would still reference the client.
Your mappings are wrong. When you define the collection in Client class, you should indicate the field in the AddressClient class which points back to the Client and that is the field client.
#OneToMany(fetch = FetchType.LAZY, mappedBy = "client", cascade = CascadeType.ALL)
private List<AddressClient> addressClientList = new ArrayList<>();
And in ClientAddress class you have:
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="id_client")
private Client client;
You do not need the field fkIdClientAddress
In some cases, you still can keep column
#Column
private Integer fkIdClientAddress;
But you have to set data for this column instead of setting value for
private Client client;
But this approach is not appropriate.
I have two entities. I want a bidirectional relationship between projects and tasks.
A project has one or more tasks
A task is associated to only one project
There are my entities:
ProjectEntity.java
#Entity
#Table(name = "projects")
public class ProjectEntity {
#Id
#GeneratedValue
#Column(name = "pr_id")
private long id;
#Column(name = "pr_name", nullable = false)
private String name;
#OneToMany(cascade=CascadeType.ALL, mappedBy="project",orphanRemoval=true)
#JsonBackReference
private Set<TaskEntity> tasks = new HashSet<TaskEntity>();
// Getters, constructors, setters
TaskEntity.java
#Entity
#Table(name = "tasks")
public class TaskEntity {
#Id
#GeneratedValue
#Column(name = "ta_id")
private long id;
#Column(name = "ta_name")
private String name;
#ManyToOne
#JoinColumn(name="pr_id")
#JsonManagedReference
private ProjectEntity project;
// Getters, constructors, setters
I would like to have the list of tasks in each ProjectEntity, and in each TaskEntity the project associated.
Here, I'm using #JsonManagedReference and #JsonBackReference to stop the infinite recursion it generated, but in my ProjectEntity, I don't have the tasks list (because of the #JsonBackReference) ...
Could you help me to get back tasks list in ProjectEntity ? I heard about #JsonIdentifyInfo, but I have not managed to do with.
Hope I'm understandable :)
The solution that I know is really using '#JsonIdentityInfo' in instead of '#JsonBackReference' and '#JsonManagedReference'.
You have to remove the '#JsonBackReference' and '#JsonManagedReference' to use '#JsonIdentityInfo'.
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
#Entity
#Table(name = "projects")
public class ProjectEntity {
#Id
#GeneratedValue
#Column(name = "pr_id")
private long id;
#Column(name = "pr_name", nullable = false)
private String name;
#OneToMany(cascade=CascadeType.ALL, mappedBy="project",orphanRemoval=true)
private Set<TaskEntity> tasks = new HashSet<TaskEntity>();
}
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
#Entity
#Table(name = "tasks")
public class TaskEntity {
#Id
#GeneratedValue
#Column(name = "ta_id")
private long id;
#Column(name = "ta_name")
private String name;
#ManyToOne
#JoinColumn(name="pr_id")
private ProjectEntity project;
}
You could opt for implement a custom serializer for the list ProjectEntity.tasks. But you must controll/stop the serialization cycle at some point; this will depend on your requirements.
Here is an example of what I mean.
#Entity
#Table(name = "projects")
public class ProjectEntity {
...
#OneToMany(cascade=CascadeType.ALL, mappedBy="project", orphanRemoval=true)
#JsonSerialize(using = CustomListSerializer.class)
private Set<TaskEntity> tasks = new HashSet<TaskEntity>();
}
And the CustomListSerializer could be something like this,
public class CustomListSerializer extends JsonSerializer<List<TaskEntity>>{
#Override
public void serialize(List<TaskEntity> tasks, JsonGenerator generator, SerializerProvider provider)
throws IOException, JsonProcessingException {
generator.writeStartArray();
for (TaskEntity task : tasks) {
generator.writeStartObject("taskEntity")
.write("id", task.id)
.write("name", task.name)
.write("project", task.project.id) //<- here you stop the cycle
.writeEnd();
}
generator.writeEnd();
}
}
Note that it is basically the example of the mentioned mini guide but it serialize information of the elements of project's tasks list instead of the tasks' id only.
I am new to JPA and stuggles with defining the relations between my classes. I have a class called Player and a class called Game. A game holds references to two Player instances. The question is, how should this be modelled?
This is my current code:
#Entity
#Table(name = "t_player")
#JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public class Player {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Basic
#Column(name = "name")
private String name;
#Basic
#Column(name = "uuid")
private final String uuid = UUID.randomUUID().toString();
I think this is ok, but my problem is in the Game class:
#Entity
#Table(name = "t_game")
#JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public class Game {
public Game() {
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Basic
#Column(name = "uuid")
private final String uuid = UUID.randomUUID().toString();
#OneToOne
#PrimaryKeyJoinColumn
#JoinColumn(name = "id")
private Player player_1;
#OneToOne
#PrimaryKeyJoinColumn
#JoinColumn(name = "player_2")
private Player player_2;
public Game(Player player_1, Player player_2) {
this.player_1 = player_1;
this.player_2 = player_2;
}
}
This is not working, my table t_game only has two field; id and uuid. Where is my problem?
Remove the PrimaryKeyJoinColumn annotation, as I don't think it is what you meant to use, as it conflicts with the joincolumn definition. Use the joincolumn annotation instead to define the foreign key field name and the field it references if necessary.