I mapped two entities to those following classes :
#Getter
#Entity
#Table(name = "users")
#JsonIdentityInfo(generator = PropertyGenerator.class,
property = "id")
#SequenceGenerator(name = "id-generator", sequenceName = "seq_users")
#EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)
#NoArgsConstructor(access = PROTECTED)
#RequiredArgsConstructor
public class User extends IdentifiedById {
#Include
#NonNull
#Column(name = "email_address", unique = true)
private String emailAddress;
#Setter
#JsonIgnore
private String hash;
#Setter
private boolean admin;
#OneToMany(
mappedBy = "user",
orphanRemoval = true,
cascade = ALL
)
#JsonIgnoreProperties("user")
private Set<Cart> carts;
{
carts = new HashSet<>(0);
}
}
#Getter
#Entity
#Table(
name = "carts",
uniqueConstraints = #UniqueConstraint(
columnNames = {
"creation_time",
"user_id"
}
)
)
#JsonIdentityInfo(generator = PropertyGenerator.class,
property = "id")
#SequenceGenerator(
name = "id-generator",
sequenceName = "seq_carts"
)
#EqualsAndHashCode(
callSuper = false
)
#RequiredArgsConstructor
#NoArgsConstructor(access = PROTECTED)
public class Cart extends IdentifiedById {
#NonNull
#Column(name = "creation_time")
private LocalDateTime creationTime;
#NonNull
#ManyToOne(cascade = ALL)
#JoinColumn(
name = "user_id",
referencedColumnName = "id"
)
#JsonManagedReference
private User user;
#Exclude
#JsonProperty("productStoreQuantities")
#JsonSerialize(converter = AdditionConverter.class)
#OneToMany(mappedBy = "cart", orphanRemoval = true, cascade = ALL)
private Set<Addition> additions;
{
additions = new HashSet<>(0);
}
}
If I retrieve a user, its carts do not contain its reference, it is fine by me.
Now from a rest endpoint perspective I would like not to serialize users along with their carts if one requests multiple users like so :
**/api/users -> {"id":1, "emailAddress":"test#test.test", "admin": false}**
**/api/users/1 -> {"id":1, "emailAddress":"test#test.test", "admin": false, "carts": [...]}**
Thus, I created a wrapper class named Users containing a list of users annotated with #JsonValue and #JsonIgnoreProperties("carts") :
#RequiredArgsConstructor
public class Users implements Serializable, List<User> {
#Delegate
#JsonValue
#JsonIgnoreProperties("carts")
private final List<User> values;
}
I don't know why but carts keep being serialized, I heard that #JsonIgnoreProperties does not work on collections and arrays but it does in my first case.
You should use JsonIgnoreProperties in a class level.
This is well explained in this post
https://www.baeldung.com/jackson-ignore-properties-on-serialization
Related
I'm developing an application that queries a database.
There are a few issues right now.
history.isPresent() == false when calling Optional<History> findByKeyEquals() intermittently. but value is exist on database
This is the information I got while tracking the issue.
All child entities are non-null.
In most cases, if the same function is re-executed, it is searched.
But sometimes it doesn't happen intermittently.
i think that i use incorrectly table relationship annotation (#OneToMany,#ManyToOne options..)
I want to solve this issue.
this is my code
History (Parent)
#Table(
indexes = {
#Index(columnList = "key", unique = true),
})
#Entity
#Getter
#ToString
#Audited
public class History implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false, columnDefinition = "BIGINT UNSIGNED")
private Long id;
#Setter
#Column(nullable = false, columnDefinition = "CHAR(36)")
private String key = UUID.randomUUID().toString();
#Setter
#Temporal(TemporalType.TIMESTAMP)
#NotAudited
private Date started = new Date();
#Setter
#Temporal(TemporalType.TIMESTAMP)
#NotAudited
private Date finished;
#Setter
#OneToMany(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "history",
orphanRemoval = true)
#NotAudited
private List<Content> contents = new ArrayList<>();
...
}
Content (Child)
#Table
#Entity
#Getter
#Audited
public class Content implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false, columnDefinition = "BIGINT UNSIGNED")
private Long id;
#Setter
#Column(columnDefinition = "LONGTEXT")
#NotAudited
private String content;
#Setter
#ManyToOne(targetEntity = History.class, fetch = FetchType.Lazy, optional = false)
#Audited
private History history;
...
}
Repository
public interface HistoryRepository
extends JpaRepository<History, Long>, RevisionRepository<History, Long, Integer> {
Optional<History> findByKeyEquals(final String key);
}
I have 2 entities that relate to one another. These 2 entities should map to each other in a Many-To-Many relationship, however, I need to also have a timestamp of their respective relationship (when it happened), so I am trying to map them using an intermediary table.
Initially, the relationship was One-To-Many, but I realized that I actually need a Many-To-Many as the business logic requires this. The structure is still the same, as in there is a Parent-Child relationship, but this time, a child should have multiple parents as well.
My BaseEntity is an abstract class that contains the fields present in all the other entities:
#Data
#MappedSuperclass
public abstract class BaseEntity {
#Id
#Min(100)
#Max(Integer.MAX_VALUE)
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
#CreationTimestamp
#Column(name = "Created_At", updatable = false)
protected ZonedDateTime createdDate;
#UpdateTimestamp
#Column(name = "Updated_At")
protected ZonedDateTime updatedDate;
#NotNull
#Column(name = "Is_Active")
protected Boolean active = true;
}
Then I have my 2 entities that should relate in a Many-To-Many style. This is my first entity and should be the parent:
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "User")
#EqualsAndHashCode(callSuper = true)
#TypeDefs( {
#TypeDef(name = "json", typeClass = JsonStringType.class),
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
public class UserEntity extends BaseEntity {
#NotBlank
#Column(name = "User_Name", columnDefinition = "varchar(255) default 'N/A'")
private String userName;
#Nullable
#JoinColumn(name = "User_Id")
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<UserRole> roleList = new ArrayList<>();
}
My second entity is considered the child entity:
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "Role")
#Where(clause = "is_active = true")
#EqualsAndHashCode(callSuper = true)
public class RoleEntity extends BaseEntity {
#NotBlank
#Column(name = "Name")
private String name;
#JsonIgnore
#JoinColumn(name = "Role_Id")
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<UserRole> userList = new ArrayList<>();
}
I also have my intermediary entity:
#Data
#Entity
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Where(clause = "is_active = true")
#EqualsAndHashCode(callSuper = true)
#Table(name = "User_Role", uniqueConstraints= #UniqueConstraint(columnNames={"User_Id", "Role_Id"}))
public class UserRole extends BaseEntity {
// Adding #JsonIgnore here will only cause an error
#JoinColumn(name = "User_Id")
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false, targetEntity = UserEntity.class)
private UserEntity user;
#JoinColumn(name = "Role_Id")
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false, targetEntity = RoleEntity.class)
private RoleEntity role;
}
Problem now is that when I try to get my UserEntity, I get infinite recursion.
So far I've tried using #JsonIgnore, #JsonManagedReference, #JsonBackReference and it did not work or I simply don't know where or how to use them properly.
Recap:
2 entities mapped by Many-To-Many relationship;
Many-To-Many implemented using an intermediary entity and One-To-Many + Many-To-One associations;
Getting recursion when showing my UserEntity;
Update: I managed to get this fixed using a different approach described in my answer to this question.
I fixed this by implementing a Composite Key structure and just using the #JsonIgnore annotation:
#Getter
#Setter
#Embeddable
#EqualsAndHashCode
#NoArgsConstructor
#AllArgsConstructor
public class UserRoleKey implements Serializable {
#Column(name = "User_Id")
Long userId;
#Column(name = "Role_Id")
Long roleId;
}
This gets to be used in the intermediary entity, which now doesn't use my BaseEntity anymore.
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "User_Role", uniqueConstraints= #UniqueConstraint(columnNames={"User_Id", "Role_Id"}))
public class UserRole {
#JsonIgnore
#EmbeddedId
private UserRoleKey id;
#JsonIgnore
#MapsId("userId")
#JoinColumn(name = "User_Id")
#ManyToOne(optional = false, targetEntity = UserEntity.class)
private UserEntity user;
#MapsId("roleId")
#JoinColumn(name = "Role_Id")
#ManyToOne(optional = false, targetEntity = RoleEntity.class)
private RoleEntity role;
#CreationTimestamp
#Column(name = "Created_At", updatable = false)
private ZonedDateTime createdDate;
}
Now, for my two entities, I have this definition:
UserEntity class (definition of the role):
#Nullable
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = true)
private List<UserRole> roleList = new ArrayList<>();
RoleEntity class (definition of the user)
#Nullable
#JsonIgnore
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "role", orphanRemoval = true)
private List<UserRole> userList = new ArrayList<>();
This seems to be working and no longer returns an infinite JSON recursion.
I have two entities : Article and Category
#AllArgsConstructor
#Entity
#ToString
#Table(name = "articles")
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "title", nullable = false, unique = true)
private String title;
#Embedded
private Audit audit = new Audit();
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE})
#JoinTable(name = "articles_categories", joinColumns = {#JoinColumn(name = "articles_id")}, inverseJoinColumns = {#JoinColumn(name = "categories_id")})
#JsonManagedReference
private List<Category> categories = new ArrayList<>();
Now the entity Category
Then I have Category entity
#Getter
#Setter
#Accessors ( chain = true )
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table ( name = "category" )
public class Category {
#Id
#GeneratedValue ( strategy = GenerationType.IDENTITY )
#Column ( name = "id" )
private Long id;
#Column ( name = "name", nullable = false, unique = true )
private String name;
#ManyToMany ( fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST , CascadeType.MERGE }, mappedBy = "categories" )
#JsonBackReference
private Set<Article> categories = new HashSet<>();
}
And now the entity Embedded
#Getter
#Setter
#Embeddable
#RequiredArgsConstructor
public class Audit {
#Column ( name = "created_on" )
private Date createdOn;
#Column ( name = "updated_on" )
private Date updatedOn;
#PrePersist
public void prePersist ( ) {
createdOn = new Date ( );
}
#PreUpdate
public void preUpdate ( ) {
updatedOn = new Date ( );
}
}
In my article repository I have the following JPA.
#Repository
public interface ArticleRepository extends JpaRepository < Article, Long > {
Page<Article> findByCategoriesIn (List<Category> categories, Pageable pageable);
}
This works correctly and is returning a page of articles but I would like to order articles by creation date using #Embeeded annotation.
How could I use this ?
Try to use something like this:
Page<Article> findByCategoriesInOrderByAudit_CreatedOnDesc(List<Category> categories, Pageable pageable);
See also this and this sections of the documentation.
I am trying to implement and use the composite pattern in my system.
The problem is that I cant retrieve all the hierarchy of entities from the backend.
I am not sure what is the problem, the fetch is fine. So, I am not sure if is hibernate.
Lets see, these are my entities.
#Entity
#Table(name = "game")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "game")
public class Game extends AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Column(name = "name", nullable = false)
private String name;
#Column(name = "detail")
private String detail;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "rule_id")
private GameRule gameRule;
...
In this class I save the main "GameRule"
#Entity
#Table(name = "game_rule")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorValue("rule")
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "_class")
#JsonSubTypes({
#JsonSubTypes.Type(value = SimpleRule.class, name = "SimpleRule"),
#JsonSubTypes.Type(value = CompositeRule.class, name = "CompositeRule") })
public abstract class GameRule implements Serializable {
private static final long serialVersionUID = -4597791997254248990L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", nullable = false)
private Long id;
private String operator;
In this class I save a list of GameRules
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING)
#DiscriminatorValue("group")
public class CompositeRule extends GameRule {
private static final long serialVersionUID = 6197786758476721324L;
#ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.ALL })
#JoinTable(name = "game_rules_hierarchy",
joinColumns = #JoinColumn(name = "parent_rule_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "child_rule_id", referencedColumnName = "id"))
#OrderBy("id")
private List<GameRule> rules;
public List<GameRule> getRules() { return rules; }
public void setRules(List<GameRule> rules) { this.rules = rules; }
And now the leaf entity.
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING)
#DiscriminatorValue("simple")
public class SimpleRule extends GameRule {
private static final long serialVersionUID = 6197786758476721324L;
private String variable;
private Double value;
#ManyToOne
#NotNull
private Device device;
Now, the restController to retrieve the data
#RequestMapping(value = "/games/{id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
#Transactional
public ResponseEntity<Game> getGame(#PathVariable Long id) {
log.debug("REST request to get Game : {}", id);
Game game = gameRepository.findOne(id);
return Optional.ofNullable(game)
.map(result -> new ResponseEntity<>(
result,
HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
Now in the view I am able to receive the CompositeRule objects but nothing about the simpleRule objects.
From the UI (angular), I am using this JSON to load the hierarchy.
vm.game.gameRule = {id: null, operator: "", type:null, _class:"CompositeRule",
rules: [ {id: null, operator: "", type:null, _class:"CompositeRule", rules:
[{id: null, type:null, _class:"SimpleRule", device: "6", variable: "POWER", operator: ">", value: "100"},
{id: null, type:null, _class:"SimpleRule", device: "6", variable: "POWER", operator: ">", value: "100"}]}
]};
This is loaded succesfully in the DB. But the problem is when I try to retrieve the entire hierarchy. Currently I am receiving only the CompositeRule objects, somthing like:
vm.game.gameRule = {id: 1, operator: "", type:null, _class:"CompositeRule",
rules: [ {id: 2, operator: "", type:null, _class:"CompositeRule", rules:[]} ]};]
In the RestController I am able to see all the hierarchy objects. I am not sure what the problem is.
Thanks for reading.
MY BAD!
Everything was fine. My problem is in the console logs. It seems that the console in chrome is not showing all the hierarchy. But if I go to the network view I am able to see everything.
Thanks.
UserInfoEntity user = userRepository.findOne(1L);
ActivityInfoEntity entity = new ActivityInfoEntity();
entity.setUser(user);
user.getActivities().add(entity);
userRepository.save(user);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
System.out.println(entity.getId());// null
System.out.println(user.getActivities().size());// 1
}
});
Even after commit, the entity has no id, but add to collection create a new entity.
If I do activityRepository.save(entity); will add double to collectionHHH-6776.I need entity.getId() in this function to do something else.If I return entity, the returned entity will have an id, what's wrong ?
EDIT
saveAndFlush still null
Spring-boot 1.3.3
EDIT
The service is annotated with #Transactional.
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "activity_info")
#Where(clause = "is_del = 0")
public class ActivityInfoEntity{
#Id
#GeneratedValue
private Long id;
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Fetch(FetchMode.SUBSELECT)
#OneToMany(mappedBy = "activity", cascade = CascadeType.ALL, orphanRemoval = true)
List<UserInfoEntity> users;
}
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "user_info")
#Where(clause = "is_del = 0")
public class UserInfoEntity{
#Id
#GeneratedValue
private Long id;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "activity_id", nullable = false, updatable = false)
private ActivityInfoEntity activity;
}
EDIT
HHH-6776 Hibernate inserts duplicates into #OneToMany collection