PSQLException: ERROR: Duplicate key value violates uniqueness constraint - spring-data-jpa

I have two tables
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "car")
public class CarEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
private String name;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "manufacture_id")
private ManufactureEntity manufactureEntity;
}
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "manufacture")
public class ManufactureEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
There is already an entry named AUDI in the manufacture table.
When I try to create a new record
ManufactureEntity manufactureEntity = new ManufactureEntity();
manufactureEntity.setName("AUDI");
CarEntity car = new CarEntity();
car.setName("a6");
car.setManufactureEntity(manufactureEntity);
carService.create(car);
Then I get an error that such a record already exists
org.postgresql.util.PSQLException: ERROR: Duplicate key value violates the "manufacture_name_key" uniqueness constraint
Details: The key "(name)=(AUDI)" already exists.
It turns out that the call to this function is trying to add a new entry to the manufacture table.
How do I make this record be created only if it is not there, and if such a record already exists, then the manufacture_id field was simply assigned the key value from the manufacture table?
When adding a new CarEntity record, how can I specify that its manufacture Entity field will be assigned to an existing record in the manufacture "AUDI" table?
But if there is no "AUDI" entry in the manufacture table, then you need to create it.
Or is it impossible to implement it at the same time? But only one of the points.
Or do I misunderstand the OneToOne annotation? One entry in the car "a6" table corresponds to only one entry in the manufacturer "AUDI" table. Let's move on. One entry in the car "a8" table corresponds to only one entry in the manufacturer "AUDI" table. Or am I wrong? And here it is necessary to implement ManyToOne.
If I change the OneToOne annotation to ManyToOne, I get the same error
The key "(name)=(AUDI)" already exists.
If I change CascadeType
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
adding to the car table works, but adding to the manufacture table does not work if there is no such record there.
I.e. when I specify a specific manufacturer
manufactureEntity.setId(1L);
then writing to the car table occurs without errors.
But if I specify an unknown manufacturer
manufacture Entity.setName("AUDIM");
Then I get the error
NULL value in the "manufacture_id" column
With the CascadeType.ALL annotation, it works only if there is no such record in the manufacture table and does not work if there is one.
With the CascadeType.MERGE annotation, it works only if there is such an entry in the manufacture table and does not work if there is none.
The question remains open.
How do I make an entry in the car table and an entry in the manufacture table work?
Or is it simply not possible and I need to manually check the existing records in the manufacture table first and if it is not there, then add it, and then write to the car table?

It seems that your problem is related to trying to add a new name of "AUDI" to the column of name. Since this column contains the property of unique, you can only have one of "AUDI" in the whole column, which means that even if the id changes, it is not possible to add a new "AUDI" value.
If you still want to be able to add the same values to the column of name (which seems like you want to do), you can do so by removing the column from the table and adding a new column without the unique constraint. Or simply remove the constraint from the table.
Remove uniqueness of index in PostgreSQL

Related

spring data JPA association in Map

I'm trying to use Map in Spring Data JPA to handle the relationship to store records of equipment quantity.
I followed this guide to create the entity.
Meeting{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "meeting_id", updatable = false)
#JsonIgnore
private int id;
#ElementCollection
#MapKeyColumn(name = "equipment_type")
#MapKeyEnumerated(EnumType.STRING)
private Map<EquipmentType, Integer> equipment = new HashMap<>();
}
EquipmentType is an Enum.
This is the table for the property:
CREATE TABLE IF NOT EXISTS meeting_equipment (
meeting_id INTEGER NOT NULL REFERENCES meeting (meeting_id),
equipment_type VARCHAR(10) NOT NULL,
quantity INTEGER NOT NULL DEFAULT 0
);
Once I try to create a meeting entity, I get error ERROR:column "meeting_meeting_id" of relation "meeting_equipment" does not exist
May I know what's the problem here?
Your table meeting_equipment does not match what JPA is expecting.
It has a column meeting_id but your JPA implementation expects meeting_meeting_id
Either rename the column to the expected meeting_meeting_id or configure your mapping to use the current column name. I think this might do the trick:
#JoinTable(joinColumns={#JoinColumn(name="meeting_id")}
Of course, you probably can create your own naming strategy if you have many cases like this and want to keep your column names as they are.

JaVers, SpringDatat, JPA: Querying for Entity Update inside a Collection

I'm new to Stackoverflow, so I will make my best to conforms with usage. I was wondering if there were a way to get a complete list of changes/snapshots of a given Entity. For now it works well with edition of Singular Properties, as well as Addition and Deletion to Collection Property. But I'm unable to find when a Child Entity in the Collection Property was updated.
Given two Entities, and a LinkEntity:
#Entity
class Person {
#Id
Long id;
#OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
Set<LinkAddress> addresses;
}
#Entity
class Address {
#Id
Long id;
#OneToMany(mappedBy = "address")
Set<Address> persons;
}
#Entity
class LinkPersonAddress {
#Id
Long id;
#ManyToOne
#ShallowReference
Person person;
#ManyToOne
#ShallowReference
Address address;
String linkType;
}
My use case is following. I get a specific Person by Id #1, and then mutate the type of specific Address (ie. HOME --> WORK). I save the Person back with the modified Set and let JPA Cascade my changes. Although all Spring Data Repositories for Person, Address, and LinkPersonAddress are annotated with #JaversSpringDataAuditable, I cannot retrieve this "update" using Javers QueryBuilder with the class Person and Id #1. It makes sense as I should query the class LinkPersonAddress instead, but how can I specify that I want only the changes from LinkPersonAddress relevant to Person with Id #1.
PS: Please apologize any typos in code snippets, as I didn't write it in my Dev Environment.
Let's start from the mapping. You did it wrong, Address is a classical ValueObject (see https://javers.org/documentation/domain-configuration/#value-object) not Entity. Because:
Address doesn't have its own identity (primary key genereted by a db sequence doesn't count)
Address is owned by the Person Entity. Person with its Addresses forms the Aggregate.
When you correct the mapping, you can use ChildValueObjects filter, see https://javers.org/documentation/jql-examples/#child-value-objects-filter

Storing ManyToMany with extra column using merge method

I have an entity class that is simply a ManyToMany with extra column, as follows:
#Entity
#Table(name = "view_templates_device_types")
public class ViewTemplateDeviceTypeEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#ManyToOne
#JoinColumn(name = "view_template_id")
private ViewTemplateEntity viewTemplate;
#Id
#ManyToOne
#JoinColumn(name = "device_type_id")
private DeviceTypeEntity deviceType;
#Column(name = "priority", nullable = false)
private int priority;
public ViewTemplateDeviceTypeEntity() {
}
...
}
I noticed that when I create a new object of this type, set viewTemplate and deviceType to values that have corresponding data in the db and I call entityManager.persist(entity) the data is stored. But when I call entityManager.merge(entity) instead of persist I get an exception:
SQL Error: 1048, SQLState: 23000
Column 'view_template_id' cannot be null
I thought that calling merge should result with data inserted into database in case it is not stored yet. It is quite important to me to use merge here (because of cascades). What can I do to make it work?
As per the JPA spec, section 2.4
"A composite primary key must correspond to either a single persistent field or property or to a set of such fields or properties as described below. A primary key class must be defined to represent a composite primary key. Composite primary keys typically arise when mapping from legacy databases when the database key is comprised of several columns. The EmbeddedId or IdClass annotation is used to denote a composite primary key. See Sections 11.1.17 and 11.1.22.".
So you either need #IdClass or #EmbeddedId. Anything else is non-portable and prone to error. I am very surprised of any JPA provider that does not throw out warnings for this.

Select highest/lowest value for x in Data Object

my
#Entity
#Table(name = "Creditcard")
#AdditionalCriteria( ..... )
public class Customer implements Serializable {
#Id
#Column(name ="CustomerId")
private long customerId;
#Column(name = "cardNumber");
private String cardNumber;
#Column(name = "apply_date")
private java.sql.Date date;
}
Example Table Data for CustomerID 1234:
CustomerId|cardNumber|apply_date|....other fields
----------|----------|----------|----------------
0000000123|0000000001|2013-01-01|----------------
0000000123|0000000002|2013-09-10|----------------
Yes, I know, the Primary Key has to be a Composite Key (EmbeddedID), but I still have to figure it out.
Due to the #AdditionalCriteria I only get 1 entry (because the other card is "banned")
but I need to get the 'apply_date' from cardNumber '1'.
Is something like that possible?
Like:
#Column(name = "apply_date")
#GetMinValue(field_name = "CustomerId")
private java.sql.Date date;
Thanks in advance!
First, your entity should represent a row in the database, not all rows. So your entity probably should be a "CreditCard" entity, using "cardNumber" as the primary key, or what ever uniquely identifies the database row.
Then, since CustomerId seems to be a foreign key probably to a table that has customer data, you would have a Customer Entity that has a 1:M relationship to CreditCards. This customer entity could then have a transient date attribute that you set in a JPA postLoad event, getting the value from a JPQL query : "select cc.date from CreditCard cc where cc.customerId = :customerId";
Setting up an Customer entity that only uses a single card/row from a CreditCard table seems like a bad idea, as what will you do when the customer gets another creditCard assigned - it is the same customer, but a new card. If you use separate entity objects, you just keep the same Customer entity and assign a new creditcard object.

JPA #Jointable does not create column for id

I'm working on JBoss AS 7 using JPA to have a List of Beans in a Entity-Bean like this:
#Entity
class section {
#Id
#GeneratedValue
private Long id;
#ManyToOne
private List<Component> components;
// ...
The table to join the two tables gets created, but it does not contain an Id, which leads to JPA creating a unique-constrain on one of the columns (SECTION_ID). Which is not really what I want, because one section can have more than one component. One component can be used in more than one section too.
I already tried
#JoinTable(name="SECTION_COMPONENT",
joinColumns = {
#JoinColumn(name="section_id", unique = false)
},
inverseJoinColumns =
#JoinColumn(name="component", unique = false)
}
I guess JPA needs at least one unique column, so it just adds that to the last column if nothing else is specified. I'd be fine with adding a new column "id" to set up a primary (or unique) key. But I am not sure how to do that.
Thanks a lot for any help
The mapping is not correct: #ManyToOne in your case means that you have one component that has many sections:
#ManyToOne
private Component component;
According to your description, you need an #ManyToMany relationship:
#ManyToMany
private List<Component> components;