I'm developing an example web-application, using JPA 2.0 entities, Hibernate 3.6.2 and Spring 3. The example contains two tables in a one-to-one relationship, the parent entity is Client and the child is Address, the PK in Address references the Client table (identifying relationship).
Running the JUnit tests, I've noted a peculiar problem with these two entities, the problem it's that the child entity persists with the (parentId + 1), my mappings are as follows:
#Entity
public class Client implements Serializable{
private Long clientId;
private Address address;
//Other fields
#Id
#GeneratedValue
public Long getClientId(){return this.clientId;}
public void setClientId(Long id){this.clientId=id;}
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="clientid",referencedColumnName="fk_clientid")
public Address getAddress(){return this.address;}
}
And the child entity:
#Entity
public class Address implements Serializable{
private Long fkClientId;
private Client client;
//Other fields
#Id
#GeneratedValue
public Long getFkClientId(){return this.fkClientId;}
public void setFkClientId(Long id){this.fkClientId=id;}
#OneToOne(mappedBy="address")
public Client getClient(){return this.client;}
}
In my test methods I link both objects using their setters, but after persist the entities and execute the line:
assertEquals(client.getClientId, client.getAddress().getFkClientId);
The test fails with the exception
java.lang.AssertionError: expected:<654> but was:<655>
I've readed similar questions and problems, but almost all of them are from JPA 1.0, it's supossed that in JPA2.0 the shared keys are automatically assigned. What am I missing?
The correct version of such a mapping is shown in javadoc of #OneToOne. Note that the side with derived identity should be the owning side of relationship (without mappedBy):
#Entity
public class Client implements Serializable {
#Id
#GeneratedValue
private Long clientId;
#OneToOne(mappedBy = "client", cascade = CascadeType.ALL)
private Address address;
...
}
#Entity
public class Address implements Serializable {
#Id
private Long fkClientId;
#OneToOne
#MapsId
#JoinColumn(name = "fk_clientid")
private Client client;
...
}
Related
I have an #Entity class, with an #Id annotation and a #OneToOne annotation on the same field. Usually this would not be a problem, but the entity class of the field with these annotations uses a composite key. This is causing more complications than I anticipated.
Here is the entity class that is posing the problem:
#Entity
public class ReportDetails implements Serializable {
#Id
#OneToOne
private MachineLine machineLine;
}
And here is the MachineLine entity class that is being used as an ID in ReportDetails:
#Entity
#IdClass(MachineLine.MachineLineKey.class)
public class MachineLine {
#Id
#ManyToOne
private Machine machine;
#Id
private long lineIndex;
public static class MachineLineKey implements Serializable {
private Machine machine;
private long lineIndex;
}
}
I have left out any extra fields and the getters and setters from these class definitions, to save space.
When I try to run my application it gives the following exception:
java.lang.IllegalArgumentException: This class [class ReportDetails] does not define an IdClass
When I put an #IdClass annotation on ReportDetails it then requires defining the individual fields of whatever class I define in #IdClass, like in MachineLine. However, I am trying to avoid doing this, in favour of having the whole MachineLine entity returned whenever a ReportDetails entity is retrieved from the database.
Is there a way of having MachineLine as the ID field of ReportDetails, without having to define extra fields within ReportDetails?
This is what JPA calls a "derived identity". You might try something like this:
ReportDetails:
#Entity
public class ReportDetails implements Serializable {
// all attributes map by the relationship: AttributeOverride is not allowed
#EmbeddedId
private MachineLine.Id id;
#MapsId
#JoinColumns({
#JoinColumn(name="machineId", referencedColumnName="machineId"),
#JoinColumn(name="machineLineIndex", referencedColumnName="index")
})
#OneToOne
private MachineLine machineLine;
// ...
}
MachineLine:
#Entity
public class MachineLine {
#EmbeddedId
private Id id;
#MapsId("machineId") // maps machineId attribute of embedded id
#ManyToOne
private Machine machine;
// ...
#Embeddable
public static class Id implements Serializable {
private long machineId; // corresponds to PK type of Machine
private long index;
// ...
}
}
Machine:
#Entity
public class Machine {
#Id
private long id;
#OneToMany(mappedBy = "machine")
private List<MachineLine> lines;
// ...
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.
So in this example scenario I have an attendance DTO, and a worker DTO, workers in this context are separated by department, and a worker can only ever be inside of one department. It is important to note that Worker {id='123-123-123', department='a'} is different to Worker {id='123-123-123', department='b'}, despite them both sharing the same Id.
I have the following class setup to try and separate functions by id and department
public class IdAndDepartmentPK implements Serializable {
private String id;
private String department;
public IdAndDepartmentPK() {}
...
}
This key class is shared between DTOs that require both the Worker's id and department, below are the two DTOs that are causing a problem.
#Entity
#IdClass(IdAndDepartmentPK.class)
public class AttendencetDto {
#Id private String id; // This is a departmentally unique attendenceId
#Id private String department;
#Column private String workerId;
#JoinColumns({
#JoinColumn(name = "workerId"),
#JoinColumn(name = "department")
})
#ManyToOne(fetch = FetchType.EAGER)
private WorkerDto workerDto;
....
}
#Entity
#IdClass(IdAndDepartmentPK.class)
public class WorkerDto {
#Id
private String id;
#Id
private String department;
...
}
WorkerDto does not need to have knowledge of AttendencetDto, but AttendencetDto, does need to have access to WorkerDto and the other data it contains.
Hibernate complains that fields like workerId should be mapped with insert="false" update="false", but if I was to do this then I wouldn't be able to persist those values to the database.
I essentially want to have those fields available whilst also having the WorkerDto available, is this possible?
You should remove #Column private String workerId; because you already map it by relation to WorkerDto.
If you want to create relation between that you should use setWorkerDto method in your AttendencetDto and just save. After transaction ends you will have your relation in DB.
I saw similar questions, but answers weren't helpful. So, i get this error:
Use of #OneToMany or #ManyToMany targeting an unmapped class: com.podro.model.Journey.roadWay[com.podro.model.RoadElement]
I'm trying to create List with objects of RoadElements (which is interface for class Point and Section). There is any other way to do it? From what i know, i guess that is the only way to create proper mapping for this classes, and have list of this elements.
#Entity
#Table(name="Journey")
public class Journey {
// Some other fields
#Column(name="road_way")
#ManyToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
private List<RoadElement> roadWay;
}
#MappedSuperclass
public interface RoadElement {}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name="Point")
public class Point implements RoadElement{
#Id
#Column(name="id")
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
private String name;
#Column(name="time_in_days")
private int timeInDays;
private Rate rating;
}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name="Section")
public class Section implements RoadElement{
#Id
#Column(name="id")
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name="section_name" , length=100)
private String sectionName;
#Column(name="time_in_days")
private int timeInDays;
#Column(name="kind_of_transport")
private Locomotion kindOfTransport;
}
Thanks for answers, I would be very grateful for help!
Associations are between entities. RoadElement is not an entity. It's an interface.
You may not do what you're trying to do. Hibernate needs to know the type of the entities contained in roadWay.
So, RoadElement should be a class, annotated with #Entity, having an ID that uniquely identifies a RoadElement among all the road elements (sections, points, etc.)
Section and Point should extend from RoadElement, and should NOT have their own ID, since it's inherited from RoadElement.
I am using Spring Data JPA and Hibernate as a provider. I've created several Repository classes which extends to JPARepository<Entity,Serializable> class. I am failing at the moment when I am fetching one entity it brings attached / connected entities along with it ! which are either connected via #OneToOne #OneToMany etc. How can I avoid fetching those connected entities ?
I have tried with #OneToMany(fetch=FetchType.LAZY) etc but still no luck. Following are my java code:
Repository
public interface TicketRepository extends JpaRepository<Ticket, Integer>{
}
Ticket Entity
#Entity
#Table(name = "tbl_tickets")
public class Ticket {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#Column(name = "customer", nullable = false, length = 256)
private String customer;
#OneToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
#JoinColumn
private User creator;
// ... other properties
}
Service
#Service
public class TicketService {
public Ticket save(Ticket obj,String id) {
User user = userService.findById(Integer.valueOf(id));
obj.setCreator(user);
Ticket savedTicket = ticketRepository.save(obj);
}
}
savedTicket always fetches User entity as well which I do not want to. How could I achieve this ?
Thanks
Get Lazy loading working on nullable one-to-one mapping you need to let hibernate do Compile time instrumentation and add a #LazyToOne(value = LazyToOneOption.NO_PROXY) to the one-to-one relation.
#OneToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
#JoinColumn
#LazyToOne(value = LazyToOneOption.NO_PROXY)
private User creator;
Hope this will work.
My domain objects are roughly like:
#Entity
public class Customer{
#Id
private String id;
private String name;
#OneToMany(cascade=CascadeType.ALL)
private List<Account> accountList;
}
#Entity
public class Account{
#Id
private String id;
private String name;
#OneToMany
private List<Service> serviceList;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(nullable=false)
private Customer customer;
}
#Entity
public class Service{
#Id
private String id;
private String name;
#ManyToOne
#JoinColumn(nullable=false)
private Account account;
}
I have a transactional Spring service. I want to return an Account instance to fronthand but I don't want to send Customer and Service List informations because of bandwith issues.
When I do:
account.setServiceList(null);
It gives no error but after a while it deletes all my service list.
When I do:
account.setCustomer(null);
It says customer_id of account cannot be null.
I just want to return a transient instance without a validation. How can I handle this.
The solution to your problem is to make the entity detached, by calling entityManager.detach(accountInstance) (or entityManager.clear() if you use JPA 1.0 to detach all entities) and only THAN change anything to it. If you use Hibernate's sessions, use the Session.evict() method.
The other solution is to use a DTO, something that has only the fields that you need.
PS: I think you have an bug, in which the bidirectional relationships are missing on one side the mapped property. E.g in Customer you should have something like
#OneToMany(cascade=CascadeType.ALL, mappedBy="customer")
private List<Account> accountList;
The same with the other relationship.