How to allow duplicates in an arrayList when using JPA? - java

I keep getting "java.lang.IllegalStateException: Multiple representations of the same entity" even though I have the #Id set as true and I'm using a one to many relation on my variable.
Here are the classes which I'm trying to relate to one another:
#Entity
#Table(name = "map_area")
public class MapArea extends BasicModel {
#Id
#Column(nullable = false, unique = true)
private String name;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "area", referencedColumnName = "name")
public List<AlternativeAreaName> alternativeNames;
public MapArea() {}
public MapArea(String name) {
this.name = name;
this.alternativeNames = new ArrayList<>();
}
}
#Entity
#Table(name = "alternative_area_name")
public class AlternativeAreaName implements Serializable {
#Id
#Column(nullable = false, unique = false)
private String area;
#Column(nullable = true)
private String alternativeName;
public AlternativeAreaName(){}
public AlternativeAreaName(String area, String alternativeName) {
this.area = area;
this.alternativeName = alternativeName;
}
}
I want to have JPA create another table that relates to this one simple based on the name variable but whenever I try to add to the list and save to the DB I get
java.lang.IllegalStateException: Multiple representations of the same entity
MapArea mapArea = new MapArea("example");
AlternativeAreaName altAreaName1 = new AlternativeAreaName("example", "alt example");
AlternativeAreaName altAreaName2 = new AlternativeAreaName("example", "alt example2");
mapArea.alternativeNames.add(altAreaName2);
mapAreaRepository.save(mapArea);

You have used the private String area field as the primary key for entity AlternativeAreaName. So when you are trying to add
AlternativeAreaName altAreaName1 = new AlternativeAreaName("example", "alt example");
AlternativeAreaName altAreaName2 = new AlternativeAreaName("example", "alt example2");
Both of them have the same primary key. So it is throwing the above exception.
To generate the primary key for JPA entity, please check
https://www.objectdb.com/java/jpa/entity/id
https://docs.oracle.com/cd/E16439_01/doc.1013/e13981/cmp30cfg001.htm

Related

hibernate ManyToOne ORM in java getting duplicate rows when persisting objects on the ONE side

I am using H2 database and hibernate and I want to apply ORM with a relation manytoone.
my code:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class ComputerComponent
{ #Id
private final UUID id;
//#Column(name = "category")
#ManyToOne
#JoinColumn(name = "category_id")
private Category category;
}
public ComputerComponent(ComputerComponentBuilder builder)
{
this.id = builder.id;
this.category = builder.category;
this.name = builder.name;
this.brand = builder.brand;
this.price = builder.price;
this.quantity = builder.quantity;
//this.category = category;
}
#Entity
public class Category
{
Category(String name)
{
this.name = name;
}
#Id
#GeneratedValue
private int category_id;
private String name;
#OneToMany(mappedBy = "category")
private List<ComputerComponent> computerComponents = new ArrayList<>();
Now the problem is that when Im creating a cpu object which extends the Computer component, I have to persist the category object and the cpu object, but when persisting category object I am getting duplicate values (ex: id 1 -> CPU , id 2 -> CPU) which gives two categories with same name but different ID , which is not what I am looking for.
Main:
Category cpuCategory = new Category("CPU");
Category cpuCategory2 = new Category("CPU");
ComputerComponentBuilder builder = new ComputerComponentBuilder(UUID.fromString("d34dbb8a-6692-4e53-97db-345137c71bc3"), cpuCategory, "Intel Core i7-8809G", "Intel", 150, 25);
builder.productLine("Core i7").numOfCores(4).prClockSpeed("3.10 GHz").grClockSpeed("1.20 GHz")
.dimension("N/A").resolution("N/A").color("N/A").interfaceType("N/A").size("N/A");
ComputerComponent cpu = builder.buildCpu();
ComputerComponentBuilder builder2 = new ComputerComponentBuilder(UUID.fromString("d34dbb8a-6692-4e53-97db-345137c71bc4"), cpuCategory2, "Intel Core i7-8809G", "Intel", 150, 25);
builder2.productLine("Core i7").numOfCores(4).prClockSpeed("3.10 GHz").grClockSpeed("1.20 GHz")
.dimension("N/A").resolution("N/A").color("N/A").interfaceType("N/A").size("N/A");
ComputerComponent cpu2 = builder2.buildCpu();
em.persist(cpu);
em.persist(cpu2);
em.persist(cpuCategory);
em.persist(cpuCategory2);
Note that I can't create one cpuCategory object , because it doesn't make sense creating fixed objetc for each Item and passing it to the builder which will cause more code and algorythm.
Furthermore I want to do the same for the Brand field, but I'm still trying to find a solution for the Category issue.

Spring ManyToMany Composite Key

I created the following models:
"Vendor"
"PickupStation"
And both of them have a OneToMany Relationship to a composite Key
"PickupStationVendorDetails" which has an embedded Id "PickupStationVendorKey"
It works to save the "PickupStationVendorDetails" with the corresponding Vendor and PickupStation but when I want to fetch them from e.g the Vendor nothing is found.
Vendor.java
#Entity
public class Vendor {
...
#OneToMany(mappedBy = "vendor")
private Set<PickupStationVendorDetails> pickupStations;
}
PickupStation.java
#Entity
public class PickupStation {
#OneToMany(mappedBy = "pickupStation")
private Set<PickupStationVendorDetails> vendors;
}
PickupStationVendorDetails.java
#Entity
public class PickupStationVendorDetails {
#EmbeddedId
private PickupStationVendorKey id;
#ManyToOne
#MapsId("vendorId")
#JoinColumn(name = "vendor_id")
private Vendor vendor;
#ManyToOne
#MapsId("pickupStationId")
#JoinColumn(name = "pickup_station_id")
private PickupStation pickupStation;
}
PickupStationVendorKey.java
#Embeddable
public class PickupStationVendorKey implements Serializable {
#Column(name = "vendor_id", columnDefinition = "BINARY(16)")
private UUID vendorId;
#Column(name = "pickup_station_id")
private Long pickupStationId;
public PickupStationVendorKey() {
}
public PickupStationVendorKey(UUID vendorId, Long pickupStationId) {
this.vendorId = vendorId;
this.pickupStationId = pickupStationId;
}
....
}
How I persist the entities:
At first I create the embeddedID and save the details via repository:
PickupStationVendorDetails pickupStationVendorDetails = new PickupStationVendorDetails();
pickupStationVendorDetails.setVendor(vendor);
pickupStationVendorDetails.setPickupStation(pickupStation);
pickupStationVendorDetails.setDeliveryDays(relationship.getDeliveryDays());
PickupStationVendorKey embeddedId = new PickupStationVendorKey(vendor.getId(),pickupStation.getId());
pickupStationVendorDetails.setId(embeddedId);
PickupStationVendorDetails d = pickupStationVendorDetailsRepository.save(pickupStationVendorDetails);
Afterwards I add them to the Set<> of the corresponding Entities and save them too.
vendor.getPickupStations().add(d);
pickupStation.getVendors().add(d);
vendorService.save(vendor);
pickupStationRepository.save(pickupStation);
And when I try to call vendor.getPickupStations() there seems to be no relationship.
Except I call pickupStationVendorDetailsRepository.findAll() the composite Key is correctly persisted and saved, and from there on I would be able to get the PickupStation and the Vendor. But that's not how it should work I guess.
Am I missing something?

Hibernate saves additional row instead of updateing and needs two saves

Note: for simplyfication i have changed some variables names and get rid of unnecessary code to show my issue.
I have two repositories:
#Repository
public interface CFolderRepository extends CrudRepository<CFolder, Long>, QuerydslPredicateExecutor<CFolder> {}
#Repository
public interface CRepository extends JpaRepository<C, Long>, CFinder, QuerydslPredicateExecutor<C> {}
The class C is:
#FilterDef(name = "INS_COMPANY_FILTER", parameters = {#ParamDef(name = "insCompanies", type = "string")})
#Filter(name = "INS_COMPANY_FILTER", condition = " INS_COMPANY in (:insCompanies) ")
#NoArgsConstructor
#AllArgsConstructor
#Audited
#AuditOverrides({#AuditOverride(forClass = EntityLog.class),
#AuditOverride(forClass = MultitenantEntityBase.class)})
#Entity
#Table(name = "INS_C")
#Getter
public class C extends MultitenantEntityBase {
#OneToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "C_FOLDER_ID")
private CFolder cFolder;
public void addFolder(List<String> clsUrl){
this.cFolder = CFolder.createFolder(clsUrl);
}
}
CFolder is:
#Getter
#NoArgsConstructor
#Audited
#AuditOverride(forClass = EntityLog.class)
#Entity
#Table(name = "C_FOLDER")
#AllArgsConstructor
public class CFolder extends EntityBase {
#Column(name = "CREATION_FOLDER_DATE_TIME", nullable = false)
private LocalDateTime creationFolderDateTime;
#Column(name = "UPDATED_FOLDER_DATE_TIME")
private LocalDateTime updatedFolderDateTime;
#Column(name = "FOLDER_CREATED_BY", nullable = false)
private String folderCreatedBy;
#Column(name = "FOLDER_UPDATED_BY")
private String folderUpdatedBy;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "cFolder", fetch = FetchType.EAGER)
#NotAudited
private Set<FolderDocument> folderDocuments = new HashSet<>();
public static CFolder createFolder(List<String> clsUrl){
CFolder cFolder = new CFolder(LocalDateTime.now(), null, SecurityHelper.getUsernameOfAuthenticatedUser(), null, new HashSet<>());
createFolderDocuments(clsUrl, cFolder);
return cFolder;
}
public void updateFolder(List<String> clsUrl){
this.updatedFolderDateTime = LocalDateTime.now();
this.folderUpdatedBy = SecurityHelper.getUsernameOfAuthenticatedUser();
this.folderDocuments.clear();
createFolderDocuments(clsUrl, this);
}
private static void createFolderDocuments(List<String> clsUrl, CFolder cFolder) {
int documentNumber = 0;
for (String url : clsUrl) {
documentNumber++;
cFolder.folderDocuments.add(new FolderDocument(cFolder, documentNumber, url));
}
}
}
FolderDocument is:
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Audited
#AuditOverride(forClass = EntityLog.class)
#Entity
#Table(name = "FOLDER_DOCUMENT")
public class FolderDocument extends EntityBase {
#ManyToOne
#JoinColumn(name = "C_FOLDER_ID", nullable = false)
private CFolder cFolder;
#Column(name = "DOCUMENT_NUMBER", nullable = false)
private int documentNumber;
#Column(name = "URL", nullable = false)
private String url;
}
And finally we have a service in which i use these entities and try to save/load them to/from database:
#Service
#AllArgsConstructor(onConstructor = #__(#Autowired))
public class CFolderService {
private final CRepository cRepository;
private final CommunicationClServiceClient communicationServiceClient;
private final CFolderRepository cFolderRepository;
public List<ClDocumentDto> getClCaseFolder(Long cId) {
C insCase = cRepository.findCById(cId);
List<ClDocumentDto> clDocumentsDto = getClDocuments(insCase.getCNumber()); // here, the object has one cFolder, but many FolderDocument inside of it
return clDocumentsDto;
}
#Transactional
public void updateCFolder(Long cId) {
C insC = cRepository.findCById(cId);
List<ClDocumentDto> clDocumentsDto = getClDocuments(insC.getCNumber());
List<String> clsUrl = clDocumentsDto.stream().filter(c -> "ACTIVE".equals(c.getCommunicationStatus())).map(ClDocumentDto::getUrl).collect(Collectors.toList());
if (Objects.isNull(insC.getCFolder())) {
insC.addFolder(clsUrl);
} else {
insC.getCFolder().updateFolder(clsUrl);
}
cFolderRepository.save(insC.getCFolder()); // here it saves additional FolderDocument instead of updateing it
cRepository.save(insC); // need second save, so can get these collection in getClaimCaseFolder successfully
}
}
I have two issues inside. In the example i was trying to clear the objects that i found from DataBase and create new ones.
1)
First is that i have to make two save operation to successfully restore the object in getClCaseFolder method (outside transactional).
2)
Second is that everytime i am saving - i get additional FolderDocument object pinned to CFolder object inside C object. I want to clear this collection and save new one.
I am not sure why hibernate does not update this object?
EDIT:
I think that i do sth like:
cRepository.save(insC);
instead of this.folderDocuments.clear();
i can do:
for(Iterator<FolderDocument> featureIterator = this.folderDocuments.iterator();
featureIterator.hasNext(); ) {
FolderDocument feature = featureIterator .next();
feature.setCFolder(null);
featureIterator.remove();
}
But i get eager fetching, why lazy wont work? There is an error using it.
Check whether you are setting ID in that Entity or not.
If ID is present/set in entity and that ID is also present in DB table then hibernate will update that record, But if ID is not present/set in Entity object the Hibernate always treat that object as a new record and add new record to the table instead of Updating.

Hibernate: detect linked field and save an object

How can I save object into database and simultaneously detect linked topic_id in the grammar.topics by topic_name?
public void updateResult(String topic_name, int userId) {
Session session = sessionFactory.getCurrentSession();
Beginner bgn = new Beginner();
bgn.setUserId(userId);
bgn.setScore(100);
// how to detect `topic_id` here by `topic_name` from the `grammar.topics`
bgn.setTopic_id( ... );
session.save(bgn);
}
#Entity
#Table(name = "beginner", uniqueConstraints =
{#UniqueConstraint(columnNames = {"user_id", "topic_id"})})
public class Beginner implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "user_id", nullable = false)
private int user_id;
#Id
#Column(name = "topic_id", nullable = false)
private int topic_id;
#Column(name = "score", nullable = false)
private int score;
//getters&setters
If in your method, you have not the id for Topic entity and you need it for persisting your Beginner entity, you must retrieve that information from the database.
If in Topic table , topic_name is UNIQUE. Easy : do a query where you retrieve the single Topic entity or the topicId with this topicName value. Ideally, you could avoid the query by keeping the topicId value when you retrieve the topic name information.
You could so replace topicName information in your processing by topicId information to be able to set the relation when you want to persist your Beginner instance. :
public void updateResult(String topicId, int userId)
instead of
public void updateResult(String topic_name, int userId)

Hibernate QueryException

Hello I am trying to reference in a Criteria a property of a composite key which is defined as and #Embeddable on an Entity
#Entity
#Table(name = "B_J_P")
public class BJP implements java.io.Serializable {
private BJPId id;
private BJI bJI;
public BJP() {
}
public BJP(BJPId id, BJI bJI) {
this.id = id;
this.bJI = bJI;
}
#EmbeddedId
#AttributeOverrides( {
#AttributeOverride(name = "jIId", column = #Column(name = "J_I_ID", nullable = false)),
#AttributeOverride(name = "kN", column = #Column(name = "K_N", nullable = false, length = 100)),
public BJPId getId() {
return this.id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "J_I_ID", nullable = false, insertable = false, updatable = false)
public BJI getBJI() {
return this.bJI;
}
}
I need to reach the kName from the following:
#Embeddable
public class BJPId implements java.io.Serializable {
private long jIId;
private String kName;
public BJPId() {
}
public BJPId(long jIId, String kN) {
this.jIId = jIId;
this.kN = kN;
}
#Column(name = "J_I_ID", nullable = false)
public long getJIId() {
return this.jIId;
}
#Column(name = "K_NAME", nullable = false, length = 100)
public String getKName() {
return this.kName;
}
}
But when I am trying to reach it from the base class where BJP is a property with the following Criteria
DetachedCriteria timestampFilter = DetachedCriteria.forClass(BJP.class)
.createAlias("id","alias")
.add(Restrictions.eq("alias.kName","DataSetName"))
.setProjection(Projections.property("kName"));
I get the following error:
org.hibernate.QueryException: Criteria objects cannot be created directly on components. Create a criteria on owning entity and use a dotted property to access component property: id
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getPathInfo
How should I formulate the criteria query in order to reach the kName property to apply filtering based on it in a dynamic sql context ?
If I have not provided enough relevant information, please ask what have I forgotten to provide full context.
EDIT: Upon Genzetto advice I have managed to reach the elements(at least it is not giving errors now) but returns no results once I do this:
DetachedCriteria timestampFilter = DetachedCriteria.forClass(BJP.class)
.add(Restrictions.eq("id.kName","DataSetName"))
.setProjection(Projections.property("id.kName"));
Session currentSession = sessionFactory.getCurrentSession();
Criteria query = currentSession.createCriteria(BJI.class)
.add(Subqueries.propertyEq("bJP",timestampFilter))
as upon looking at the SQL it is of the format
... where this_.J_INST_ID = (select this_.K_NAME as y0_ from .B_J_P this_ where this_.K_NAME=?)
it is trying to add the subquery to the ID of the root object although I want it part of bJP. How can I add it to proper location ?
You don't need to use an alias to do this. You can access directly to the composite key attributes:
DetachedCriteria timestampFilter = DetachedCriteria.forClass(BJP.class)
.add(Restrictions.eq("id.kName","DataSetName"))
.setProjection(Projections.property("id.kName"));

Categories

Resources