I want to persist a list with key-values pairs but I do not know how I can reach my objective with JPA. I can implement it with simple jdbc but I want to use jpa.
Requirements:
- prue JPA, I do not want to see any hibernate, eclipselink, etc. related imports in the java classes
- keys need to be unique in the database
- if the parent is deleted than value needs to be deleted but I need to keep keys in the database
- there can be same items in the list (same keys with different values OR same keys with same values)
Database tables:
product
id, name, ...
1 car type 1
2 apple
3 chair
attribute-key
id, key, last_used (key with unique constraint!!)
10 category
11 color
12 quality
13 packing
14 price
attribute
id, prod_id, key_id, value
50 1 10 small
51 1 11 green
52 1 14 1 million eur
53 2 13 12 pieces
54 2 11 red
55 3 12 poor
...
use cases:
If the "car type1" is deleted from the database then I want to keep keys in the attribute table. For example attribute with id 10 is only belongs to this product so I can delete this item from database without any sql exception (key is not used by other product) but I want to keep it in the table.
New product with new keys: no problem, jpa store them
New product with existing key:
product.name=product A
attributes: color-white|price-11usd|resolution-big
New keys need to be inserted into the attribute-key table ("resolution") and after that persist values into the attribute table.
Product with duplicated keys (and values) needs to be stored:
product.name=product B
attributes:color-red|color-green|color-green
Product with duplicated NEW keys (keys are not persisted yet)
product.name=product C
attributes:attr1-value1|attr1-value2|...
My code works fine but there are two use-cases when it falls:
There are items in the list with same keys and the key is not persisted yet (case 5). Jpa iterates on the list and persists the new key. When jpa wants to persist the next item with same key I get a "ERROR: duplicate key value violates unique constraint" exception because key is defined in the database as a unique field and it is stored in the first round.
There is an item in the list with a key which is existing in the database. Exception: Multiple representations of the same entity
Code:
public void update(Product product)
{
try
{
Product queryResult = (Product) em.createNamedQuery("Product.findBy...")....getSingleResult();
// existing product, perform update
queryResult.getAttributes().clear();
updateAttributes(product.getAttributes());
Product managedProduct = em.merge(product);
...
}
catch (NoResultException e)
{
// new product, perform insert
updateAttributes(product.getAttributes());
Product managedProduct = em.merge(product);
...
}
catch (Exception e)
{
}
}
private void updateAttributes(final List<Attribute> attributes)
{
for (Attribute attribute : attributes)
{
attribute.setId(null);
try
{
AttributeKey queryResult = (AttributeKey) em.createNamedQuery("AttributeKey.findByKey")
.setParameter("key", attribute.getKey().getKey()).getSingleResult();
//attribute.setKey(queryResult);
attribute.getKey().setId(queryResult.getId());
}
catch (NoResultException e)
{
//em.persist(attribute.getKey());
attribute.getKey().setId(null);
}
attribute.getKey().setLastUsed(new Date());
}
}
@Entity
@Table(name = "ATTRIBUTE")
public class Attribute
{
@Id
@SequenceGenerator...
@GeneratedValue...
@Column(name = "ID", unique = true, nullable = false)
private Long id;
@ManyToOne(cascade = {CascadeType.ALL}, optional = false, fetch = FetchType.EAGER)
@JoinColumn(name = "ATTRIBUTE_KEY_ID", nullable = false, updatable = false)
private AttributeKey key;
...
}
@Entity
@Table(name = "ATTRIBUTE_KEY")
@NamedQueries(...)
public class AttributeKey
{
@Id
@SequenceGenerator(...)
@GeneratedValue(...)
@Column(name = "ID", unique = true, nullable = false)
private Long id;
@Column(name = "KEY", length = 128, unique = true)
private String key;
@Column(name = "WEIGHT")
private Long weight;
@Column(name = "LAST_USED")
private Date lastUsed;
...
}
Is it possible to implement this logic with jpa?