I have Spring Data Entity:
#Entity
#Table(name = "price_alert")
#Data
public class PriceAlert {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "customer_id")
private Long customerId;
}
So, for insert/update this entity perfectly fit for me: no need to load entity by id (here: for save PriceAlert it would be needed to load Customer).
But for some selects I also want to retrieve Customer. Currently I solve this problem by having 2 entities with 2 different corresponding repositories. In first entity all fields are plain Long values. In second I use Mapped value (like bellow).
#Entity
#Table(name = "price_alert")
#Data
public class PriceAlertExtended {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne(optional = false)
#JoinColumn(name = "customer_id", referencedColumnName = "id")
private Customer customer;
}
With both #Column and #JoinColumn in entity on create I get SQLException: Field 'customer_id' doesn't have a default value
So my question is about: is there any better approach to this problem?
For your particular use case. I would suggest you to look at the following snippet
#Entity
#Table(name = "price_alert")
#Data
public class PriceAlertExtended {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "customer_id", referencedColumnName = "id")
private Customer customer;
#Column(name = "customer_id", insertable = false, updatable = false, nullable = false)
private Long customerId;
}
Here, I have used fetch = FetchType.LAZY in #ManyToOne, this will save a JOIN on Customer Table until to access it by person.getCustomer().
And, I have created an extra field to access customerId to just get the customer_id. Make sure to mention nullable = false, insertable = false and updatable = false
Related
I have two entities BookingLegEntity and BookingEntity which reference each other. But anytime I try to retrieve them from the database (e.g. via findByUuid), BookingLegEntity.belongsTo remains null.
Here are my entities:
#Entity
#Table(name = "BOOKING_LEG")
#SQLDelete(sql = "UPDATE BOOKING_LEG SET deleted = true WHERE id=?")
#Where(clause = "deleted=false")
public class BookingLegEntity {
#Id
#Column(name = "ID", unique = true, updatable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "UUID", nullable = false)
private UUID uuid;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "BELONGS_TO")
private BookingEntity belongsTo;
// ..
#ManyToOne
#JoinColumn(name = "DISTRIBUTOR")
private DistributorEntity distributor;
#Column(name = "TRANSPORT_TYPE")
#Convert(converter = TripTypeEnumConverter.class)
private TripTypeEnum transportType;
// ...
}
#Entity
#Table(name="BOOKINGS")
#SQLDelete(sql = "UPDATE BOOKINGS SET deleted = true WHERE id=?")
#Where(clause = "deleted=false")
public class BookingEntity {
#Id
#Column(name="ID", unique=true, updatable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name="BOOKING_ID")
#Convert(converter = BookingIdConverter.class)
private BookingId bookingId;
#ManyToOne
#JoinColumn(name ="BOOKED_BY")
private UserEntity bookedBy;
// ..
#OneToMany(mappedBy = "belongsTo", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<BookingLegEntity> bookingLegs = new HashSet<>();
// ...
}
Here is my repository:
#Repository
public interface BookingLegRepository extends JpaRepository<BookingLegEntity, Long> {
Optional<BookingLegEntity> findByUuid(UUID id);
// ...
}
The values in the database itself look correct:
What is really strange is that this has worked before (belongsTo was not null) but suddenly stopped working. Does anyone has any idea as to what we might do wrong here?
Do not use cascade = CASCADEType.ALL on your ManyToOne annotation, because removing one BookingLeg will cause a removal of all in corresponding Booking
The solution should be to use
cascade = CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH}) in its stead.
I would Truncate Cascade or Delete from Bookings where original_itinerary is null before i move on to using the new entities.
Sincerely hope it helps. (No hate if it doesn't pls)
Edit : i didnt see that comment by #dey, its my own. :P saw his comment after posting my ans
I would like to ignore #OnetoMany field in my entity. fetch data need to get actual fields but don't want to fire query to dependent table. But deleting data from parent table needs deletion from dependent table
I have tried #Transient that ignores but the delete is also being ignored. Is there any other option to tell JPA not to fetch data from childs table when i call the parent entity?
#Entity
Table(name = "User")
public class UserEntity implements Serializable {
#Id
#Column(name = "id")
private int id;
#Column(name = "SERIAL", unique = true, nullable = false)
private String serial;
#OneToMany(mappedBy = "serialBySerialId", cascade = CascadeType.ALL)
private Set<UserActionEntity> userActionsById;
}
#Table(name = "user_action")
public class UserActionEntity implements Serializable {
#Id
#Column(name = "id")
private int id;
#Column(name = "action")
private String action;
#ManyToOne
#JoinColumn(name = "USER_ID", referencedColumnName = "ID", nullable = false)
private UserEntity userByUserId;
If you don't want to fire query to dependent table, you can use (fetch = FetchType.LAZY) on UserActionEntity property.
#OneToMany(mappedBy = "serialBySerialId", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<UserActionEntity> userActionsById;
I'm trying to learn how to use implicit joins in JPQL. An example I'm working from seems to suggest that one can reference a foreign key as a path and then have access to attributes in the table the foreign key references.
It's a 1:M mandatory relationship between User and Report.
userId is the foreign key that references User.
I've checked that I have the right libraries imported that I have the right mapping (#JoinColumn, #ManyToOne, #OneToMany, mappedBy, etc), everything seems to be okay.
#GET
#Path("findByTotalCalBurnedAndHeight/{height}/{totalCalBurned}")
#Produces({"application/json"})
public List<Report> findByTotalCalBurnedAndHeight(#PathParam("height") Integer height, #PathParam("totalCalBurned") Integer totalCalBurned) {
TypedQuery<Report> q = em.createQuery("SELECT r FROM Report r WHERE r.totalCalBurned = :totalCalBurned AND r.userId.height = :height", Report.class);
q.setParameter("height", height);
q.setParameter("totalCalBurned", totalCalBurned);
return q.getResultList();
}
As seen above I'm trying to access the "height" attribute in the "User" table with: r.UserId.height
Based on the example I'm working from I'd expect the join to work here however the result I'm getting is this error: "The state field path 'r.userId.height' cannot be resolved to a valid type."
Am I missing something here? Any feedback is much appreciated.
Update to show mapping:
In report class:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "REPORT_ID")
private Integer reportId;
#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")
#ManyToOne(optional = false)
#Basic(optional = false)
#NotNull
#Column(name = "USER_ID")
private Integer userId;
In user class:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "USER_ID")
private Integer userId;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "userId")
#Basic(optional = false)
#NotNull
#Column(name = "HEIGHT")
private int height;
The problem is this part:
r.userId.height
In order for this to work, userId must be an entity. You can define a relationship with user field in Report and write something like:
"SELECT r FROM Report r left join r.user u WHERE r.totalCalBurned = :totalCalBurned AND u.height = :height"
Edit - change this:
#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")
#ManyToOne(optional = false)
#Basic(optional = false)
#NotNull
#Column(name = "USER_ID")
private Integer userId;
into:
#JoinColumn(name = "USER_ID")
#ManyToOne(optional = false)
#Basic(optional = false)
#NotNull
private User user;
I have the following POJOs for my db schema.
#Entity
#Table(name = "Quotes")
public class QuoteRequest
{
public QuoteRequest(){}
#Id
#Column(name = "quote_request_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long QuoteRequestId;
#OneToMany(mappedBy = "quoteRequest", cascade = CascadeType.ALL)
#OrderColumn(name = "accidents_id")
private Accidents[] accidents;
// Getters and setters
}
#Entity
#Table(name = "Accidents")
public class Accidents
{
public Accidents()
{
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "accidents_id")
private long AccidentId;
#Column(name = "amount", nullable = false)
private Float amount;
#ManyToOne(optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "quote_request_id", nullable = false)
private QuoteRequest quoteRequest;
// Getters and setters
}
Because I'm using an array to store Accidents[] Hibernate is requiring me to add #OrderColumn. Adding this causes an update to be generated after insert that zeros out my accidents_id. The only way I found around that is to change Accidents[] to Set.
How can I keep Accidentsas an array and not have Hibernate force this 2nd update after the insert?
I created a #ManyToMany relation between two table, without have a relational entity through them.
Something like this:
#Entity
public class Category{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "CATEGORY_ID", unique = true, nullable = false)
private Long categoryId;
}
#Entity
public class Content {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "CONTENT_ID", unique = true, nullable = false)
private Long contentId;
#ManyToMany(fetch=FetchType.LAZY)
#JoinTable(
name="CATEGORIES_CONTENTS",
joinColumns={
#JoinColumn(name="CONTENT_ID",
referencedColumnName="CONTENT_ID",
nullable = false)},
inverseJoinColumns={
#JoinColumn(name="CATEGORY_ID",
referencedColumnName="CATEGORY_ID",
nullable = false)
})
private List<Category> categories;
}
Now, after the end of the project, I have the need to add an extra field in the relational table to handle a new feature.
Is it possible to do that, without create the related entity? I don't want to change a lot of things.
I already read this good article: Can add extra field(s) to #ManyToMany Hibernate extra table?
but it isn't what I need.
Tanks a lot, Davide.