I'm trying to make spring boot application. i want to get set of goals for each user. But i receive the error:
java.lang.StackOverflowError: null
at java.base/java.util.HashMap.hash(HashMap.java:339) ~[na:na]
at java.base/java.util.HashMap.get(HashMap.java:552) ~[na:na e.t.c.
In Goal:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
#OnDelete(action = OnDeleteAction.CASCADE)
private User userss;
In User:
#OneToMany(mappedBy = "userss", fetch = FetchType.EAGER)
#OnDelete(action = OnDeleteAction.CASCADE)
private Set<Goal> goalSet = new HashSet<>();
And method for receiving goals for each user:
#Override
public Set findByUserId(Long userId){
return (Set) getSession().createQuery("from Goal where userss.id =:userId").setParameter("userId", userId).stream().collect(Collectors.toSet());
}
And method for receiving all users with their goals:
#Override
#SuppressWarnings("unchecked")
public Set getGoals() {
return (Set) getSession().createQuery("from Goal").stream().collect(Collectors.toSet());
}
I use lombok for hash (and actually for other basic methods)
If you are using Lombok for your entity, you must limit the generator
#Entity
#Data
#EqualsAndHashCode(onlyExplicitlyIncluded = true) // important
public class User {
#Id
#EqualsAndHashCode.Include // important, only on the PK
UUID id;
// Other fields not included in the generator
}
This is because, by default, Lombok generates the methods Equals and HashCode for every fields of your entity, but Hibernate requires the equality to be only done on the #Id field.
You have to do this for all your Entities.
Why was it crashing?
Because Goal references User, and User references Goal; when the default generator implemented Equals and HashCode, it would call both instances back and forth infinitely, until the crash.
Even without such cyclic references, Hibernate wouldn't know how to handle the entities in its cache without a proper Equals and HashCode.
You may need a similar limitation for #ToString to prevent recursive infinite loops.
Related
I'm having issues with defining a foreign key field within an entity. One specific thing that I can't find an answer to, is how to define such field but as a Long type, and not as that target entity type, and also set it up as ON DELETE CASCADE.
E.g.
#Entity
#Table(name = "user")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
and
#Entity
#Table(name = "address")
public class AddressEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#JoinColumn(
table = "user",
name = "user_id",
referencedColumnName = "id")
private Long userId;
}
This example works fine, but now one can't easily define this DELETE ON CASCADE for the userId field i.e. Address entity.
One specific thing that I can't find an answer to, is how to define
such field but as a Long type, and not as that target entity type, and
also set it up as ON DELETE CASCADE.
It stands to reason that you cannot find an answer, because JPA does not provide one. If you want JPA to manage relationships between entities, then you must define those relationships in the JPA way, with entities holding references to other entity objects and declaring appropriate relationship annotations.* And if you want cascading deletes in your persistence context then you definitely do want them to be managed / recognized by JPA, for any other kind of approach is likely to create problems involving the context falling out of sync with the underlying data store.
It's unclear what problem you are trying to solve by avoiding JPA-style relationship management, but I'm inclined to think that there must be a better way. For example, if you want to avoid requiring the persistence context to load the associated UserEntity whenever an AddressEntity is loaded, then you would define the relationship with a lazy fetch strategy:
#Entity
public class AddressEntity {
// ...
#OneToOne(optional = true, fetch = FetchType.LAZY)
private UserEntity user;
}
#Entity
public class UserEntity {
// ...
#OneToOne(optional = true, fetch = FetchType.LAZY, cascade = CascadeType.ALL,
mappedBy = user)
AddressType address;
}
(Do note, however, that FetchType.LAZY is a hint, not a constraint. The context might sometimes still load the user together with its address if that's convenient.)
If you want to get the associated user id from an address, then the best way to do so is to read it from the user:
// ...
public Long getUserId() {
return (user == null) ? null : user.getId();
}
That does require the UserEntity to define an accessible getId() method, but since you are using JPA field-based access, you do not need also to provide a setter, and you may give the method default access. Or you could just declare UserEntity.id such that it is directly accessible by AddressEntity.
On the other hand, if you want to provide for the user ID to be accessible without loading the user entity then instead of a method such as the above getUserId(), in addition to the relationship field you could define a persistent, read-only AddressEntity.userId field, mapped to the appropriate column. It must be read-only because the value of the id in the underlying data store will necessarily be managed via the entity relationship, so it cannot also be managed via this separate field. For example:
#Entity
public class AddressEntity {
// ...
#OneToOne(optional = true, fetch = FetchType.LAZY)
private UserEntity user;
#Column(name = "user_id", insertable = false, updatable = false, nullable = true)
public Long userId;
}
This is a brittle approach, and I do not recommend it. It will be prone to problems with the userId field falling out of sync with the user entity. That may be bearable for the usage you have in mind, but this sort of weirdness is fertile ground for future bugs.
*Side note: as far as I know or can determine, JPA does not define semantics for a #JoinColumn annotation on a non-relationship field such as in your original code. That doesn't mean that your particular persistence provider can't interpret it in a way that you characterize as "works fine", but at minimum you are on thin ice with that.
I am writing a Spring Boot application that will use Hibernate/JPA to persist between the app and a MySQL DB.
Here we have the following JPA entities:
#MappedSuperclass
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private Long id;
#Type(type="uuid-binary")
private UUID refId;
}
#Entity(name = "contacts")
#AttributeOverrides({
#AttributeOverride(name = "id", column=#Column(name="contact_id")),
#AttributeOverride(name = "refId", column=#Column(name="contact_ref_id"))
})
public class Contact extends BaseEntity {
#Column(name = "contact_given_name")
private String givenName;
#Column(name = "contact_surname")
private String surname;
#Column(name = "contact_phone_number")
private String phone;
}
#Entity(name = "assets")
#AttributeOverrides({
#AttributeOverride(name = "id", column=#Column(name="asset_id")),
#AttributeOverride(name = "refId", column=#Column(name="asset_ref_id"))
})
public class Asset extends BaseEntity {
#Column(name = "asset_location")
private String location;
}
#Entity(name = "accounts")
#AttributeOverrides({
#AttributeOverride(name = "id", column=#Column(name="account_id")),
#AttributeOverride(name = "refId", column=#Column(name="account_ref_id"))
})
public class Account extends BaseEntity {
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "contact_id", referencedColumnName = "contact_id")
private Contact contact;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "asset_id", referencedColumnName = "asset_id")
private Asset asset;
#Column(name = "account_code")
private String code;
}
And the #RestController, where an Account instance will be POSTed (to be created):
public interface AccountRepository extends CrudRepository<Account, Long> {
#Query("FROM accounts where account_code = :accountCode")
public Account findByCode(#Param("accountCode") String accountCode);
}
#RestController
#RequestMapping(value = "/accounts")
public class AccountController {
#Autowired
private AccountRepository accountRepository;
#RequestMapping(method = RequestMethod.POST)
public void createNewAccount(#RequestBody Account account) {
// Do some stuff maybe
accountRepository.save(account);
}
}
So the idea here is that "Account JSON" will be sent to this controller where it will be deserialized into an Account instance and (somehow) persisted to the backing MySQL. My concern is this: Account is a composition (via foreign keys) of several other entities. Do I need to:
Either create CrudRepository impls for each of these entities, and then orchestrate save(...) calls to those repositories such that the "inner-entitities" get saved first before the "outer" Account entity?; or
Do I just save the Account entity (via AccountRepository.save(account)) and Hibernate/JPA automagically takes care of creating all the inner/dependendent entities for me?
What would the code/solution look like in either scenario? And how do we specify values for BaseEntity#id when it is an auto-incrementing PK in the DB?
That depends on your design and specific use cases, and what level of flexibility you want to keep. Both ways are used in practice.
In most CRUD situations, you would rather save the account and let Hibernate save the entire graph (the second option). Here you usually have another case which you didn't mention, and it is updating of the graph, which you would probably do the same way, and actually the Spring's repository save method does it: if the entity is a new (transient) one, it persists it, otherwise it merges it.
All you need to do is to tell Hibernate to cascade the desired entity lifecycle operations from the Account to the related entities:
#Entity
...
public class Account extends ... {
#OneToOne(..., cascade = {CascadeType.PERSIST, CascadeType.MERGE})
...
private Contact contact;
#OneToOne(..., cascade = {CascadeType.PERSIST, CascadeType.MERGE})
...
private Asset asset;
...
}
However, you pay the penalty of reloading the object graph from the db in case of merge operation, but if you want everything done automatically, Hibernate has no other way to check what has actually changed, other than comparing it with the current state in the db.
Cascade operations are applied always, so if you want more flexibility, you obviously have to take care of things manually. In that case, you would omit cascade options (which is your current code), and save and update the parts of the object graph manually in the order that does not break any integrity constraints.
While involving some boilerplate code, manual approach gives you flexibility in more complex or performance-demanding situations, like when you don't want to load or reinitialize the parts of the detached graph for which you know that they are not changed in some context in which you save it.
For example, let's assume a case where there are separate web service methods for updating account, contact and asset. In the case of the account method, with cascading options you would need to load the entire account graph just to merge the changes on the account itself, although contact and asset are not changed (or worse, depending on how you do it, you may here revert changes on them made by somebody else in their dedicated methods in the meantime if you just use the detached instances contained in the account).
Regarding auto-generated ids, you don't have to specify them yourself, just take them from the saved entities (Hibernate will set it there). It is important to take the result of the repository's save method if you plan to use the updated entity afterwards, because merge operation always returns the merged copy of the passed-in instance, and if there are any newly persisted associated entity instances in the updated detached graph, their ids will be set in the copy, and the original instances are not modified.
While testing implementation of JPA into Spring I found out that my query is querying twice instead of once.
#Data
#Entity
#Table(name = "superfan_star")
public class Star implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
private int id;
private String name;
private String nickname;
private String description;
private String thumbnail;
private String backgroundImage;
private Date created;
private Date updated;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "starId", referencedColumnName = "id", insertable = false, updatable = false)
private Set<Media> medias;
}
This is model class.
#Service
public class SuperfanStarService
{
#Autowired
private StarRepository starRepository;
#PersistenceContext
private EntityManager em;
#Transactional
public List<Star> getStars()
{
QStar qStar = QStar.star;
QMedia qMedia = QMedia.media;
List<Star> stars =
new JPAQuery(em)
.from(qStar)
.where(qStar.id.eq(19))
.list(qStar);
return stars;
}
}
This is my service class.
20160915 20:52:59.119 [http-nio-8080-exec-1] DEBUG j.sqlonly - org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:82)
9. select star0_.id as id1_2_, star0_.background_image as backgrou2_2_, star0_.created as created3_2_, star0_.description as
descript4_2_, star0_.name as name5_2_, star0_.nickname as
nickname6_2_, star0_.thumbnail as thumbnai7_2_, star0_.updated as
updated8_2_ from superfan_star star0_ inner join superfan_media
medias1_ on star0_.id=medias1_.star_id where star0_.id=19
20160915 20:52:59.173 [http-nio-8080-exec-1] DEBUG j.sqlonly - org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:82)
9. select medias0_.star_id as star_id11_2_0_, medias0_.id as id1_1_0_, medias0_.id as id1_1_1_, medias0_.created as created2_1_1_,
medias0_.description as descript3_1_1_, medias0_.end_time as
end_time4_1_1_, medias0_.is_approve as is_appro5_1_1_,
medias0_.is_approved_final as is_appro6_1_1_, medias0_.is_pushed as
is_pushe7_1_1_, medias0_.is_represent as is_repre8_1_1_,
medias0_.length as length9_1_1_, medias0_.released as release10_1_1_,
medias0_.star_id as star_id11_1_1_, medias0_.teleport_media_id as
telepor12_1_1_, medias0_.thumbnail as thumbna13_1_1_, medias0_.title
as title14_1_1_, medias0_.work_end as work_en15_1_1_,
medias0_.work_start as work_st16_1_1_, medias0_.youtube_id as
youtube17_1_1_, medias0_.youtube_title as youtube18_1_1_ from
superfan_media medias0_ where medias0_.star_id=19
As you can see, it's querying twice instead of once, probably because of inverse update? Is there any way to make my JPA model query only once?
This works as expected. The first query gets the Star entity with id = 19 from the database, and the second query gets the linked Media entities for that Star entity from the database. (Carefully look at the log of the SQL statements to understand what is being queried).
Note that you specified FetchType.EAGER on the medias field in class Star:
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "starId", referencedColumnName = "id", insertable = false, updatable = false)
private Set<Media> medias;
Eager fetching means that when you do a query for one or more Star objects, Hibernate immediately gets the linked Media objects - as opposed to lazy fetching, which means that the second query will not be done immediately, but only when necessary (when you access the medias member variable).
While there is an accepted answer I suspect there maybe something else at play here. I note you have a Lombok #Data which I believe overrides equals() and hashcode() based on all fields which is dangerous in a JPA entity as it can trigger lots of additional data being loaded when associated items are added to hash based collections.
Yeah I found out that Lombok is causing problems for lists as it's querying medias for each Star. I'm trying to see if there's a way to use Lombok without querying everything but there doesn't seem to be a way.
Firstly, I would suggest not implementing equals() and hashcode() based on all fields of your entity: that is the root cause of your problem and makes no sense anyway - base them on a unique business key if you have one available. Essentially two entities are equal if they have the same id but see here however:
The JPA hashCode() / equals() dilemma.
Additionally, hashcode() should be based on immutable fields - see here:
http://blog.mgm-tp.com/2012/03/hashset-java-puzzler/.
Lombok's #Data just aggregates other individual annotations. So you can remove it, use the individual #Getter #Setter and #ToString Lombok annotations and write your own sensible implementations of equals() and hashcode() when required:
https://projectlombok.org/features/Data.html
I'm using PlayFramework 2.3 and i have the following classes:
MyEntity.java:
#Entity(name = "myentity")
public class MyEntity extends Model {
#Id
#GeneratedValue
public long id;
#OneToOne(cascade = CascadeType.ALL, optional = true)
#JoinColumn(name = "actual_version_id", nullable = true)
public Version actualVersion;
#OneToOne(optional = true)
#JoinColumn(name = "next_version_id", nullable = true)
public Version nextVersion;
...
}
Version.java
#Entity(name = "version")
public class Version extends Model {
#Id
#GeneratedValue
public long id;
#OneToOne
#JoinColumn(name = "entity_id", nullable = false)
public MyEntity entity;
...
}
When i want to make a new version for the entity, i copy it by detach, set the id to 0, and persist like this:
public Version clone(){
JPA.em().detach(this);
this.id = 0;
JPA.em().persist(this);
return this;
}
If i use the following code it works properly (first code)
entity.nextVersion = entity.actualVersion.clone();
JPA.em().flush();
entity.actualVersion = entity.nextVersion;
entity.nextVersion = null;
JPA.em().flush();
I didn't really like this code, because i could use it like this (second code)
entity.actualVersion = entity.actualVersion.clone();
JPA.em().flush();
But if I do this the foreign key doesn't update in the 'entity' table and i don't know why. Can anybody tell me what's the difference between the two implementtation of cloning? It seems some JPA black magic for me, but i couldn't find the answer.
EDIT:
This is a refactored code to make it easier to undestand. I'm not overriding any function from the Object class, or any other (my clone() function is called newRound in the original code with 2 parameters for example)
I don't really want to make any model modification like adding CascadeType.ALL to the annotation, or anything like that, because this is a program in production now, and i don't know what bugs would that make.
I just want to know why the first code updates the foreign key in the entity (actual_version_id) and the second doesn't. I think it has to be something with that CascadeType.ALL annotation parameter at the actualVersion variable.
Be very careful with clone() in a JPA setting; the JPA environment usually adds tracking properties to the bytecode of a class, and might get confused. Instead, override the default clone() to create an actual object and copy over all properties manually, one-by-one.
I am having an issue working with Hibernate and enforcing unique data members when inserting.
Here are my abridged Entity objects:
Workflow:
#Entity
public class Workflow {
private long wfId;
private Set<Service> services;
/** Getter/Setter for wfId */
...
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "workflow_services",
joinColumns = #JoinColumn(name = "workflow_id"),
inverseJoinColumns = #JoinColumn(name = "service_id"))
public Set<Service> getServices() {
return services;
}
Service:
#Entity
public class Service {
private long serviceId;
private String serviceName;
/** Getter/Setter for serviceId */
...
#Column(unique=true,nullable=false)
public String getServiceName() {
return serviceName;
}
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "service_operations",
joinColumns = { #JoinColumn(name = "serviceId") },
inverseJoinColumns = { #JoinColumn(name = "operationId") })
public Set<Operation> getOperations() {
return operations;
}
Operation:
#Entity
public class Operation {
private long operationId;
private String operationName;
/** Getter/Setter for operationId */
#Column(unique=true,nullable=false)
public String getOperationName() {
return operationName;
}
My issue:
Although I have stated in each object what is SUPPOSED to be unique, it is not being enforced.
Inside my Workflow object, I maintain a Set of Services. Each Service maintains a list of Operations. When a Workflow is saved to the database, I need it to check if the Services and Operations it currently uses are already in the database, if so, associate itself with those rows.
Currently I am getting repeats within my Services and Operations tables.
I have tried using the annotation:
#Table( uniqueConstraints)
but have had zero luck with it.
Any help would be greatly appreciated
The unique or uniqueConstraints attributes are not used to enforce the uniqueness in the DB, but create the correct DDL if you generate it from hibernate (and for documentation too, but that's arguable).
If you declare something as unique in hibernate, you should declare it too in the DB, by adding a constraint.
Taking this to the extreme, you can create a mapping in which the PK is not unique in the DB, and hibernate will throw an exception when it tries to load one item by calling Session.load, and sudently finding that there are 2 items.
Inside my Workflow object, I maintain a Set of Services. Each Service maintains a list of Operations. When a Workflow is saved to the database, I need it to check if the Services and Operations it currently uses are already in the database, if so, associate itself with those rows.
I think you're asking Hibernate to detect duplicate objects when you add them to the Set, yes? In other words, when you put an object in the Set, you want Hibernate to go look for a persistent version of that object and use it. However, this is not the way Hibernate works. If you want it to "reuse" an object, you have to look it up yourself and then use it. Hibernate doesn't do this.
I would suggest having a helper method on a DAO-like object that takes the parent and the child object, and then does the lookup and setting for you.