I was creating my classes for a project using a chart for practice purposes until I stumbled upon this order_items:
I had no problem creating an Entity like Orders or Products because I knew that for Orders I just had to do something like:
#Entity
#Table(name = "orders")
public class Orders {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "order_id")
private Integer orderId;
// rest of the code...
}
And for for Products something like:
#Entity
#Table(name = "products")
public class Products {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "product_id")
private Integer productId;
// rest of the code...
}
But the table order_items has the variables order_id and item_id, does that count as a composite key? If that is the case, how should those variables look in my OrderItems class?
#Entity
#Table(name = "order_items")
public class OrderItems {
#Column(name = "order_id")
private Integer orderId;
#Column(name = "item_id")
private Integer itemId;
// rest of the code...
}
I've checked different questions and they mention using #IdClass or #EmbeddableId for composite keys, but I'd like to confirm first if that is what I should do in this situation, unless it's not the case, maybe there are more approaches.
I'd really appreciate opinions and/or any article related to this, thank your for your time.
As you mentioned you can use #EmbeddableId.
Here is example :
#Embeddable
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder(toBuilder = true)
public class OrderItemsPK implements Serializable {
private static final long serialVersionUID = 1L;
#Column(insertable = false, unique = false, updatable = false, nullable = false,name = "order_id")
private Long orderId;
#Column(insertable = false, unique = false, updatable = false, nullable = false,name = "products_id")
private Long productsId;
}
And the Order Items Class.
#Entity
public class OrderItems {
#EmbeddedId
private OrderItemsPK id;
#OneToOne
#JoinColumn(name = "products_id", nullable = false, unique = false, insertable = false, updatable = false, referencedColumnName = "products_id")
private Products products;
#OneToOne
#JoinColumn(name = "orders_id", nullable = false, unique = false, insertable = false, updatable = false, referencedColumnName = "orders_id")
private Order order;
private Long itemId;
}
Related
I want to join a single column from another table to one of my #Entity classes:
Currently it works as follows:
#Entity
public class Product {
#Id
private long id; //autogenerated
String type; //used for mapping
#ManyToOne
#JoinColumn(name = "type", insertable = false, updatable = false)
ProductMapping mapping;
}
#Entity
public class ProductMapping {
#Id
String type;
String longname;
}
Question: how could I replace the #ManyToOne mapping to directly map to String longname?
//TODO: how to directly map to 'mapping.longname'?
#JoinColumn(name = "type", insertable = false, updatable = false)
String mapping.longname;
You can use #Formula annotation with a query like this :
#Formula("(select pm.longname from product_mapping pm where pm.COL_NAME = value)")
private String longName;
//give the column name of type from product_mapping table
OR
You can also use the below approach :
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "type", referencedColumnName = "COL_NAME", insertable = false, updatable = false)
ProductMapping mapping;
The other entity use #NaturalId annotation on the field.
#Entity
public class ProductMapping {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
String type;
#NaturalId
#Column(name = "SOME_VALUE")
String longname;
}
I used question_id as the primary key of questions table and it is a foreign key for the answers table. #JoinColumn has used for declare referencedColumnName .
#Entity
#Table(name = "questions")
public class Question {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long question_id;
#Column(nullable = false, unique = false, length = 100)
private String question_subjectArea;
#Column(nullable = false, unique = false, length = 1000)
private String fullQuestion;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn( name = "questionID", referencedColumnName = "question_id")
List<Answer> answer = new ArrayList<>();
//Getters and setters
#Entity
#Table(name = "answers")
public class Answer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long answer_id;
#Column(nullable = true, unique = false, length = 100)
private Long answer_authorID;
#Column(nullable = false, unique = false, length = 100)
private String fullAnswer;
//Getters and setters
Application.properties configuration as follows
spring.jpa.hibernate.ddl-auto=create
In our Entity class: Answer, seems you need also define the member: Question.
#ManyToOne
#JsonIgnore
#JoinColumn(name = "question", referencedColumnName = "question_id")
private Question question;
I have two entities Space as parent and SpaceInactivityDate as child with relation #OneToMany and #ManyToMany and with space_id as a foreign key.
The insert operation works fine but when I tried to update, the Space table was updated but in the SpaceInactivityDate table instead of updating rows he added new lines. I have used the CascadeType=All and cascade = {CascadeType.PERSIST,CascadeType.MERGE} but usually I had the same result.
Space.java
#Entity
#Table(name = "space")
public class Space {
#OneToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE}, mappedBy = "space", fetch = FetchType.LAZY)
private Set<SpaceInactivityDate> spaceInactivityDates = new HashSet<SpaceInactivityDate>();
// getters+setters
}
SpaceInactivityDate .java
#Table(name="space_incativity_date")
#Entity
public class SpaceInactivityDate {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private Integer id;
#Column(name = "inactivity_start_date", nullable = false, insertable = true, updatable = true)
#Type(type = "ec.ep.dit.sip.expomep.persistency.type.PersistentLocalDate")
private LocalDate inactivityStartDate;
#Column(name = "inactivity_end_date", nullable = false, insertable = true, updatable = true)
#Type(type = "ec.ep.dit.sip.expomep.persistency.type.PersistentLocalDate")
private LocalDate inactivityEndDate;
#JoinColumn(name="space_id",referencedColumnName="id")
#ManyToOne(optional=false,fetch=FetchType.LAZY)
private Space space;
}
SpaceServiceImpl.java
this.spaceDao.save(space);
There is my class TableOne.java:
#Table(name = "table_one")
#EntityListeners(AuditingEntityListener.class)
public class TableOne {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(
name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "id", unique = true, nullable = false, updatable = false)
private String id;
#CreatedDate
#Column(name = "created", nullable = false, updatable = false)
private LocalDateTime created;
#LastModifiedDate
#Column(name = "modified", nullable = false)
private LocalDateTime modified;
#Column(name = "status_desc")
private String statusDesc;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(table = "callers")
private Party caller;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(table = "callee")
private Party callee;
...getter/setter
}
And there is Part.java:
#Entity
public class Party {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false, updatable = false)
private long id;
#Column(name = "desc")
private String desc;
#Column(name = "ip")
private String ip;
#Column(name = "port")
private int port;
}
The following fields: caller, callee inside TableOne.java contains the same fields (id,desc, port, ip), so I want to keep these in two different tables. For example inside callee and caller tables.
How I can do that?
You can use two entities for that. Simply remove #Entity annotation from Party and annotate it with #MappedSuperclass. Then you can create two entities:
#Entity
#Table(name = "caller")
public class Caller extends Party
and
#Entity
#Table(name = "callee")
public class Callee extends Party
Both will have the same fields, but will be mappet to two different tables.
In my project I have to connect to existing database and do logic which updates two tables.
My setup is following:
#Entity
#Table(name = "DOCUMENTCONTENT")
#Getter
public class DocumentContent {
#Id
#Column(name = "ID", insertable = false, updatable = false)
private Long id;
#OneToOne
#JoinColumn(name = "DOCUMENT_ID", insertable = false, updatable = false)
private Document document;
#Lob
#Column(name = "CONTENT")
#Setter
private byte[] content;
}
#Entity
#Table(name = "DOCUMENT")
#Getter
public class Document {
#Id
#Column(name = "ID", insertable = false, updatable = false)
private Long id;
#OneToOne(mappedBy = "document")
private DocumentContent documentContent;
#OneToMany(mappedBy = "document", fetch = EAGER)
private List<Attachment> attachments;
}
#Entity
#Table(name = "ATTACHMENT")
#Getter
public class Attachment {
#Id
#Column(name = "ID")
private Long id;
#ManyToOne
#JoinColumn(name = "DOCUMENT_ID", insertable = false, updatable = false)
private Document document;
#ManyToOne
#JoinColumn(name = "CONTRACT_ID",updatable = false, insertable = false)
private Contract contract;
}
#Entity
#Table(name = "CONTRACT")
#Getter
public class Contract {
#Id
#Column(name = "ID", insertable = false, updatable = false)
private Long id;
#Column(name = "STATUS")
#Setter
private String status;
#ManyToOne
#JoinColumn(name = "CUSTOMER_ID", insertable = false, updatable = false)
private Customer customer;
#OneToMany(mappedBy = "contract", fetch = EAGER)
private List<Attachment> attachments;
}
#Service
public class MyServiceImpl implements MyService {
#Autowired
private DocumentContentRepository documentContentRepository; // spring data Crud Repository
#Override
#Transactional
public void updateDocumentContent(SomeDto someDto) {
DocumentContent documentContent = documentContentRepository.findByDocumentId(someDto.getDocumentId());
documentContent.setContent(someDto.getBytes());
List<Contract> contracts = documentContent.getDocument().getAttachments()
.stream().map(Attachment::getContract).collect(toList());
contracts.forEach(contract -> contract.setStatus("SIGNED"));
documentContentRepository.save(documentContent);
}
}
When I fire method from above service I can notice those SQL in console output:
Hibernate: update documentcontent set content=? where id=?
Hibernate: update contract set status=? where id=?
I understand why jpa performed first update in documentcontent table, but I don't know why it did update in contract table aswell. As you can see I didn't use CascadeType.MERGE in any entity.
Can you explain me why this second update has been performed without declaring cascade type?
I doubt it has anything to do with Cascade at all, but with transactional write behind mechanism (more info). I believe you could also get rid of the line
documentContentRepository.save(documentContent);
since you are modifying two managed entities. At the end of the transaction hibernate persists all entities marked as modified by the dirty checking mechanism (more info).
You are getting 2nd query for the reason, you are modifying Status property of Contract.
JPA detect this change and try to update entity.
This is default CaseCadeType behaviour of #OneToMany
For further reading follow this link.