Spring Data JDBC invert OneToOne navigation - java

I have an existing data scheme I'm reluctant to change. There are two entities/tables: parent and child, with parent having the foreign key column child_id. It's a 1-to-1 relationship.
The problem is: the magic behind the scenes expects the child table to have the foreign key column (the exception mentions a ...JOIN ON child.parent = parent.id). Is it possible to inverse this to match the existing scheme? (I know it is with hibernate, but I'd like to stay with JDBC).
Relevant code:
#Repository
public interface ParentRepository extends CrudRepository<Parent, Long>{
}
#Data
public class Parent {
#Id
private Long id;
private Child child;
}
#Data
public class Child {
#Id
private Long id;
}
Somewhat related question: Spring Data JDBC invert OneToMany navigation

There is currently no support for this on the Spring Data JDBC side.
On option that comes to mind is to create a view that already performs the join and has instead of triggers to perform the correct actions on the Child table.
You can then map the Child as embedded.

Related

Hibernate upgrade object from parent class to child class in JOINED inheritance

I'm using Spring Data Jpa and Hibernate on my project.
I have three tables:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
class Parent {
String id;
String name;
}
#Entity
class FirstChild extends Parent {
...
}
#Entity
class SecondChild extends Parent {
...
}
On the first step of my logic I should save Parent object without child type.
And on the second step I know to which Child table it should belong.
For example:
Parent parent = parentRepository.findById("id");
FirstChild firstChild = new FirstChild();
firstChild.setId(parent.getId());
firstChild.setName(parent.getName());
parentRepository.save(firstChild);
But when I do a Hibernate save it throws me exception:
o.h.e.i.DefaultLoadEventListener Load request found matching entity in context, but the matched entity was of an inconsistent return type; returning null
As I understand it doesn't know how to upgrade entity from parent to child type and just throws an exception because of conflict - entity with same id is already there.
Is there any solutions for this problem?
JPA is a means of mapping your Java domain model onto a relational database schema. Since there is no such thing as 'promoting a parent class to a child class' in Java, there is no support in JPA for such an operation.
That being said, you could probably achieve the desired behavior using a native update query. You would need to update the discriminator column (DTYPE) column, and insert a new row into the table corresponding to the child entity (note that in the SINGLE_TABLE strategy, updating the discriminator column would suffice).
A much better solution IMHO, is to delete the parent entity and insert a new child entity. If you're concerned about referential integrity, perhaps you should switch from inheritance to composition.

Define an inherited column to be part of composite primary key in JPA

We have an abstract base entity class that defines columns found in all of our entities (e.g. creation and modifications timestamps and related user IDs). For most of our entities, there are two related database tables: one that holds the "live" data, i.e. the current state of the entity; and one that holds the audit trail, i.e. all historical versions of the entity, as snapshots.
One of the columns shared by all entities (and defined in the base entity class) is a version number column, which is a simple running sequence that tells how many edits have been made to that particular entity. The version column is present in both the audit table and the "regular" table, but in the audit table, the version column is also part of a composite primary key, which is formed by combining the primary key of the "regular" entity and the version number. Simplified example (some entity-level annotations omitted for brevity):
#MappedSuperclass
public abstract class BaseEntity {
#Column(name = "version")
private Long version;
// Timestamps, user IDs etc.
}
#MappedSuperclass
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class PersonBase extends BaseEntity {
// All basic columns of a person defined here
}
public class Person extends PersonBase {
#Id
#Column(name = "id")
private long id;
// All references to other entities defined here
}
public class PersonAudit extends PersonBase {
#Id
#Column(name = "id")
private long id;
// FIXME: Version column should be part of composite primary key
// All foreign key columns defined here (can't have
// a direct FK relationship in an audit table, as you
// don't know which version to link to)
}
Is it possible to make the inherited version column be part of a composite primary key in the audit entities? I tried declaring the version column also in the PersonAudit table and adding the #Id annotation to it, but after that the version field in BaseEntity wasn't populated anymore. It'd be very convenient to have the field in BaseEntity as we use it in some generic Criteria Queries using the JPA static metamodel, and if we move the field declaration to the individual audit entities, we can't reference the column anymore in those queries.
P.S. We're using Hibernate, so Hibernate-specific solutions are also acceptable if JPA doesn't enable this.

How to use spring Repository without #Id?

I'm using spring CrudRepository throughout my application.
Now I want to also create one for an #Entity that does not have an #Id. Is that possible at all?
//probably ID is always required?
public interface Repository<T, ID extends Serializable>
JPA requires that every entity has an ID. So no, entity w/o an ID is not allowed.
Every JPA entity must have a primary key.
from JPA spec
You may want to read more about how JPA handles a case when there's no id on the DB side from here (see 'No Primary Key').
Alternatively you can extend AbstractPersistable<Long> for your all POJO entities.
Follow example: - https://github.com/spring-projects/spring-data-examples/blob/master/jpa/example/src/main/java/example/springdata/jpa/simple/User.java
Use #IdClass (composite key)
Yes, this is possible. This approach can be used when you read from a DB view. The below example demonstrates how to mark all (2, in the given example) columns as the composite ID:
import java.io.Serializable;
import lombok.Data; // auto-generates AllArgs contractor, getters/setters, equals, and hashcode
#Data
#Entity
#IdClass(MyEntity.class) // <--this is the extra annotation to add
#Table(name = "my_table")
public class MyEntity implements Serializable{ // <--this is the extra interface to add
#Id // annotate each column with #Id
private String column1;
#Id // annotate each column with #Id
private String column2;
}
The above MyEntity uses the full record state as the key:
--so, in addition to #EqualsAndHashcode, do flag the class with Serializable interface;
--annotate each field with #ID
In your <T, ID> repository say the ID is your full (entity) class:
interface MyEntityRepo extends CrudRepository<MyEntity, MyEntity> {
// ^^^the full record is the ID
See also:
docs.oracle.com - Example 7.4-7.5: Non-Embedded Composite Primary Key
baeldung.com/jpa-composite-primary-keys, for more fine-grained custom IdClass, with fewer (than all) columns in the ID.

Over ride default fetch to skip rows with certain values

I am using Java, Hibernate, Spring Data and fairly new to this technology. I need to figure out how to Skip rows that are marked as 'archived.' We have a strict guidance from our DB architect that no rows shall be deleted from the database.
#MappedSuperclass
public class AbstractEntity implements Identifiable<String> {
#Id
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy="uuid")
private String id;
private boolean archived; //<----?
}
#Entity
public class Employee extends AbstractEntity {
private String title;
private String fullName;
#ManyToOne
private Department dept;
}
#Entity
public class Department extends AbstractEntity {
private String name;
}
In the above example, any class extending AbstractEntity should never return rows that have archived == true. All my domain classes will be extending AbstractEntity so I'd like a solution that's either implemented in AbstractEntity.java or at some global configuration so that all generated SQL calls are 'where [table].archived <> true'
Take a look at Hibernate Filters.
#FilterDef(name="activeOnly")
#Filter(name="activeOnly", condition= "archived <> 1")
// assumes that all tables have a numeric column "archived"
// as you can notice, this is a filter at the SQL Level
// (not at the Entity level)
#MappedSuperclass
public class AbstractEntity // ....
I've never used Spring Data, but the Adding custom behavior to all repositories section of the official documentation lead me to belieave that it is quite easy to obtain an injected EntityManager and customize its behaviour. Just unwrap it and enable the filter.
Session session = entityManager.unwrap(Session.class);
session.enableFilter("activeOnly");
If you want the filter to be applied for all subclasses of the #MappedSuperclass use a recent version of Hibernate. Only version 3.5 and greater (see HHH-4332) supports this behaviour.
Also, there is one gotcha, you may need to repeat the filter on associations (See Hibernate Filters on related table with MappedSuperClass).
If you want to customize the delete operations as well, use #SQLDelete to mark archived = 1 (see Soft deletes using Hibernate annotations). But to the best of my knowledge this only works on mapped entities (nothing can be done at the #MappedSuperclass level)

How to add one-to-many association in Hibernate to an embedded type?

I have an Entity that holds the last instance of a Component:
#Entity
public class Customer {
...
#Embedded
public ServiceCall getLastServiceCall() {...}
...
}
#Embeddable
public class ServiceCall() {
public Date getDate();
public Status getStatus();
}
The ServiceCall is embedded in order to call customer.getLastServiceCall().getDate() without requiring another table join.
I want to make that association one-to-many, and start saving all ServiceCalls, while holding one embedded in the customer.
Hibernate's Docs (v3.6, which I'm using) states:
You can also use association annotations in an embeddable object (ie #OneToOne, #ManyToOne, #OneToMany or #ManyToMany). To override the association columns you can use #AssociationOverride.
and it seem that all I should do is add #OneToMany to the LastServiceCall association.
Will that work for me? If not, what are my alternatives? If yes, how will that affect 2nd level cache, and is there a limitation on updating that embedded instance (I can live with an immutable objects)?
#Embeded types are not supposed to have their own identity in the database, so I don't think you can add #OneToMany to the Customer class on the ServiceCall.
#OneToMany
#Embedded
public ServiceCall getLastServiceCall() {...}
However you can add an association to the #Embeded Service call element like so.
#Entity
pubic class HistoricalServiceCall extends ServiceCall
{
#Id
private String id;
}
#Embeddable
public class ServiceCall {
#OneToMany(fetch=FetchType.LAZY)
#JoinColumn(name="join_column_defined_on_customer_table")
List<HistoricalServiceCall> getServiceCallHistory();
}
Update: putting FetchType.LAZY on the getServiceCallHistory() is a hint to the JPA provider to wait until you call getServiceCallHistory before it does another select to pull in that association.
So with the setup I am describing if you do customer.getLastServiceCall().getDate() it will not pull
in the ServiceCallHistory before the relationship is lazy.
What you need is the following:
A Customer entity
An embeddable ServiceCall
A HistoricalServiceCall entity.
The Customer should contain an embedded ServiceCall field (the last service call).
The HistoricalServiceCall entity should have an ID, an embedded ServiceCall field (the data of the HistoricalServiceCall), and, potentially, a ManyToOne association to Customer if you want the OneToMany to be bidirectional.
The Customer should have a OneToMany association to HistoricalServiceCall.

Categories

Resources