More problems with JPA deletion of row in database - java

I have the following code that defines the relationship between three tables.
public class Attachment implements Serializable {
#Id
#Column(name="attachment_id")
private int attachmentId;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="reference_id")
private Reference reference;
#OneToMany(mappedBy="attachment")
private List<Reference> references;
MORE STUFF;
}
public class Uuid implements Serializable {
#Id
#Column("name=uuid_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int uuidId;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="reference_id")
private Reference reference;
MORE STUFF
}
public class Reference implements Serializable {
#Id
#Column(name="reference_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int referenceId;
#OneToMany(mappedBy="reference")
private List<Attachment> attachments;
#ManyToOne(cascade=CascadeType.MERGE)
#JoinColumn(name="attachment_id")
private Attachment attachment;
#OneToMany(mappedBy="reference")
private List<Uuid> uuids;
MORE STUFF
}
I have some more code that picks the specific "uuid" object/row that needs to be deleted, and the idea is that anything in the other tables that needs deleting because they share the same reference_id should be deleted too. The code that does this is:
try {
final EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.remove(data);
transaction.commit();
} catch (final PersistenceException e) {
throw new CPDPersistenceException(e);
}
When the delete is performed it throws Exception "Cannot delete or update parent row: a foreign key constraint fails. I posted something on a variation of this before. Does anybody have any ideas? Thanks for your time.

You have one one-to-many relationship between Reference and Attachment defined but also a many-to-one between Reference and Attachment (and vice versus), this will never work. You should have either one of them, not both, or a many-to-many relationship.
Fix that and it should be much easier to delete objects.
For instance:
public class Attachment implements Serializable {
#Id
#Column(name="attachment_id")
private int attachmentId;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="reference_id")
private Reference reference;
//MORE STUFF;
}
public class Reference implements Serializable {
#Id
#Column(name="reference_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int referenceId;
#OneToMany(mappedBy="reference")
private List<Attachment> attachments;
#OneToMany(mappedBy="reference")
private List<Uuid> uuids;
/bMORE STUFF
}

Related

possible alternatives to #GeneratedValue with #EmbeddedId in JPA

My entity has a wrapped Identifier like this,
#Entity
public class Article {
#EmbeddedId
private ArticleId articleId;
....
}
#Embeddable
public class ArticleId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
}
in my architecture, multiple application instances(it all same.) are connected to the same data source
so #GeneratedValue(strategy = GenerationType.IDENTITY)seems good
because even if instances A and B try to create Accountat the same time, Its Id is guaranteed by the database.
problem is that #GeneratedValue is only able to use with #Id annotation (#Id is not available for EmbeddedId)
PersistenceUnitUtil.getIdentifier(Object entity) could be a alternative? like this,
ArticleId articleId = ArticleRepository.nextIdentity();
I am not sure that whether it causes the race condition.
Could PersistenceUnitUtil.getIdentifier(Object entity) guarantee the unique id across the different application instance(JVM)? I don't think so.
In this situation, What alternative is possible?
One solution could be to use an #IdClass to get rid of the nested property and be able to generate the identifier (since nested properties are "assigned" and cannot be generated, so the call of PersistenceUnitUtil.getIdentifier(Object entity) would not help here). See e.g. here for a complete guide (also linked in the linked answer by #SternK from the comment)
An #IdClass could look like this:
public class ArticleId implements Serializable {
private Long id;
}
An entity could use it:
#Entity
#IdClass(ArticleId.class) // specified dependency
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
// expose "typed" id:
public ArticleId getId() {
return new ArticleId(id);
}
}
Spring-Data #Repositorys also work with corresponding #IdClass objects, e.g.:
#Repository
public interface UserEntityRepository extends JpaRepository<Article, ArticleId> {
}
// would offer e.g. this method:
repository.findById(new ArticleId(123L));

How to create an api like this 'GET host:///api?user.username=bug' in Springboot?

I saw this kind of API style once and it worked proper
Noob here and I am current learning RESTful stuff,If anyone who may gives some advice and instruction.I'd be very appreciate!
Get URL
//The argument isn't mandatory, May be order.orderInfo,order.orderPrice etc
http://localhost:8080/order?order.orderNo=123
Controller code
#GetMapping
CollectionModel<Order> getOrders(Order order) {
List<Order> ordersResult = orderService.getOrders(order);
for (Order result : ordersResult) {
Link selfLink = WebMvcLinkBuilder.linkTo(OrderController.class).slash(result.getId()).withSelfRel();
result.add(selfLink);
}
Link link = WebMvcLinkBuilder.linkTo(OrderController.class).withSelfRel();
return CollectionModel.of(ordersResult, link);
}
Entity code
public class Order extends RepresentationModel<Order> implements Serializable {
#Id
#GeneratedValue
#Column(unique = true)
private Integer id;
private Long orderNo;
}
And my jpa repository
public interface OrderRepository extends JpaRepository<Order, Integer>,PagingAndSortingRepository<Order, Integer> {
}
I have finally figured out that this kind of URL is not work this way.
Shoud be apply to a specific situation.Which is the get endpoint method has a 'Relative Object'.Then we may use URL like this to improve flexable api.
Talk is cheap,I'll show you the code!
Entity->Customer
public class Customer extends RepresentationModel<Customer> implements Serializable {
#Id
#GeneratedValue
#Column(unique = true)
private Integer id;
private String name;
//Add relative Object to entity.
#OneToOne
private Order order;
}
Entity->Order
public class Order extends RepresentationModel<Order> implements Serializable {
#Id
#GeneratedValue
#Column(unique = true)
private Integer id;
private Long orderNo;
//Add relative Object to entity.
#OneToOne
private Customer customer;
}
Endpoint->CustomerController/getMethod
#GetMapping
CollectionModel<Customer> getCustomers(Customer customer) {
List<Customer> customersResult =
customerService.getCustomers(customer);
for (Customer result : customersResult) {
Link selfLink = WebMvcLinkBuilder.linkTo(CustomerController.class).slash(result.getId()).withSelfRel();
result.add(selfLink);
}
Link link = WebMvcLinkBuilder.linkTo(CustomerController.class).withSelfRel();
return CollectionModel.of(customersResult, link);
}
URL
scheme://[api]/[order.orderNo=123]
Then the value will mapping to paramter-custoemr inside.
PS:HTTP method '#fragment' may suitable for this case.
Thanks to #JRichardsz :)

How do I retrieve only the ID instead of the Entity of an association?

I have a class that looks something like this:
#Entity
public class EdgeInnovation {
#Id
public long id;
#ManyToOne
public NodeInnovation destination;
#ManyToOne
public NodeInnovation origin;
}
and another one that looks something like this:
#Entity
public class NodeInnovation {
#Id
public long id;
#OneToOne
public EdgeInnovation replacedEdge;
}
and so each table map to the other, so one entity will refer to other entities that will refer to more entities and so on, so that in the end there will be many entities that will be fetched from the database. Is there any way to only get the value (integer/long) of the key and not the entity it refers to? something like this:
#ManyToOne(referToThisTable="NodeInnovation")
#Entity
public class EdgeInnovation {
#Id
public long id;
#ManyToOne(referToTable="NodeInnovation")
public Long destination;
#ManyToOne(referToTable="NodeInnovation")
public Long origin;
}
and
#Entity
public class NodeInnovation {
#Id
public long id;
#OneToOne(referToTable="EdgeInnovation")
public Long replacedEdge;
}
Here's an example. I want the stuff in green, I get all the stuff in red along with it. This wastes memory and time reading from disk.
You would just map the foreign keys as basic mappings instead of Relationships:
#Entity
public class EdgeInnovation {
#Id
public long id;
#Column(name="DESTINATION_ID")
public Long destination;
#Column(name="ORIGIN_ID")
public Long origin;
}
Or you can have access to both the ID and the referenced entity within EdgeInnovation, but you'll need to decide which you want to use to set the mapping:
#Entity
public class EdgeInnovation {
#Id
public long id;
#Column(name="DESTINATION_ID", updatable=false, insertable=false)
public Long destination_id;
#ManyToOne
public NodeInnovation destination;
#Column(name="ORIGIN_ID", updatable=false, insertable=false)
public Long origin_id;
#ManyToOne
public NodeInnovation origin;
}
In the above example, the origin_id is read-only while the origin reference is used to set the foreign key in the table. Any changes though should be made to both fields to keep the object mappings in synch with each other.
Another alternative is to use the provider's native code to find if the reference is lazy and wasn't triggered, and then get the foreign key value. If it has been triggered, you can just use the reference to get the ID value, since it won't cause a query to fetch anything. This is something you would have to look into EclipseLink's source code for though.
Sorry, I cant comment so I put it here ,
I think it should be like that
#Entity
public class EdgeInnovation {
#Id
public long id;
#ManyToOne
public NodeInnovation destination;
#ManyToOne
public NodeInnovation origin;
}
And the other class is :
#Entity
public class NodeInnovation {
#Id
public long id;
#OneToMany(mappedBy="origin")
public List<EdgeInnovation> replacedEdges;
}
If I'm getting the situation wrong sorry, (Could you draw your classes with the relations so I can get it straight?)
Why not use a new construction in JPA and a custom constructor in NodeInnovation? Basically, create a transient property in NodeInnovation for use when you only want the EdgeInnovation id:
#Entity
public class NodeInnovation {
#Id #GeneratedValue private Long id;
private Integer type;
#OneToOne
private EdgeInnovation replacedEdge;
#Transient
private Long replacedEdgeId;
public NodeInnovation() {}
public NodeInnovation(Long id, Integer type, Long replacedEdgeId ) {
this.id = id;
this.type = type;
this.replacedEdgeId = replacedEdgeId;
}
...
}
Use it like so:
NodeInnovation n = em.createQuery("select new NodeInnovation(n.id, n.type, n.replacedEdge.id) from NodeInnovation n where n.id = 20", NodeInnovation.class).getSingleResult();
You didn't say how you were selecting NodeInnovation, whether directly or through a join, but either way the trick is the new NodeInnovation in the JPQL or CriteriaBuilder query.
I am aware I am quite late but some people might look for an answer to the same question - in your JPA repository you could do something like this:
#Query("SELECT new java.lang.Integer(model.id) FROM #{#entityName} model WHERE model.relationModeFieldlName.id IN :relationModelIds")
List<Integer> findIdByRelationModelIdIn(#Param("relationModelIds") List<Long> relationModelIds);

Error when trying to map a recursive relation

I'm trying to map a recursive relation using JPA. I don't know why I'm getting some errors on the #OneToMany line.
Here is a code sample:
#Entity
public class RecursiveType implements Serializable {
private static final long serialVersionUID = -2459343636539882731L;
#Id
public int id;
#OneToMany(mappedBy="rec1", cascade=CascadeType.PERSIST) //here is where I get the errors
public RecursiveType rec1;
public Map<String, Map<RecursiveType, List<Map<RecursiveType, List<InnerTypes>>>>> rec2;
//getters and setters
}
And the line of persistence.xml file
<class>pt.ptinovacao.persistencetester.model.RecursiveType</class>
I've had several errors like: "Target entity is not defined" and "The attribute type for a collection mapping must be java.util.Collection…"
Why does this happen?
I've fixed it, I just changed the annotations and the relation type
#Entity
public class RecursiveType implements Serializable {
private static final long serialVersionUID = -2459343636539882731L;
#Id
public int id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="REC1_ID", referencedColumnName="ID")
public RecursiveType rec1;
public Map<String, Map<RecursiveType, List<Map<RecursiveType, List<InnerTypes>>>>> rec2; //I really don't need to try and map this xD
//getters and setters
}

JPA Bi-directional Remove

In OpenJPA, I try to remove an entity with a bi-directional mapping to another entity. I did "find" and then "remove" but I have got an exception of "Encountered deleted object". Can someone provide me a working example?
#Entity
#Table(name="Order")
public class Order implements Serializable {
#EmbeddedId
private OrderPK pk;
...
#OneToOne(cascade=CascadeType.ALL, mappedBy="order")
private Invoice invoice;
}
#Entity
#Table(name="Invoice")
public class Invoice implements Serializable {
#EmbeddedId
private InvoicePK pk;
...
#OneToOne
#PrimaryKeyJoinColumn
private Order order;
}
#Embeddable
public class OrderPK implements Serializable {
private String id;
private Date date;
...
}
#Embeddable
public class InvoicePK implements Serializable {
private String id;
private Date date;
...
}
First, I add them in a single transaction and commit:
Order order = new Order(...);
order.set...
Invoice invoice = new Invoice(...);
invoice.set...
order.setInvoice(invoice);
invoice.setOrder(order);
em.persist(order);
Then when I try to remove the order, I expect the invoice will be gone too:
Order order = em.find(Order.class, orderPK); em.remove(order);
but I have an exception instead saying:
Encountered deleted object "org.apache.openjpa.enhance.Order$pcsubclass-
Order-OrderPK#92882281" in persistent field "Invoice.order" of managed
object "Invoice$pcsubclass-InvoicePK#92882281" during flush.

Categories