I'm trying to save a complex entity with Hibernate where we have multiple entities with composite keys.
When I try tosave it seems that Hibernate it is not retrieving correctly the values from some columns on child entities which have composite key hence postgre is returning a not-null violation error.
#Entity
#Table(name = "activities", schema = "ptw")
#Data
#TypeDefs({
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class),
#TypeDef(name = "pg-id-uuid", typeClass = PostgresIdUUIDType.class)
})
public class Activity extends AuditAtBy implements Serializable {
#EmbeddedId
private CommonId commonId;
#MapsId("siteId")
#ManyToOne
#OnDelete(action = OnDeleteAction.CASCADE)
#Type(type = "pg-id-uuid")
#JoinColumn(name="site_id",referencedColumnName = "id", columnDefinition = "uuid", updatable = false)
private Site site;
#ManyToOne()
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(formula = #JoinFormula(value="location_id", referencedColumnName = "id")),
#JoinColumnOrFormula(formula = #JoinFormula(value="site_id", referencedColumnName = "site_id"))})
#Type(type = "pg-id-uuid")
#OnDelete(action = OnDeleteAction.CASCADE)
private Location location;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "job_pack_id", referencedColumnName = "id", columnDefinition = "uuid", insertable = false, updatable = false),
#JoinColumn( name = "site_id", referencedColumnName="site_id", columnDefinition = "uuid", insertable = false, updatable = false)
})
#Type(type = "pg-id-uuid")
#OnDelete(action = OnDeleteAction.CASCADE)
private JobPack jobPack;
#Column(name = "permit_number", nullable = false, length = 10)
private String permitNumber;
#Column(name = "order_number", nullable = false)
private short orderNumber;
#Column(name = "location_name", length = 200)
private String locationName;
**#ManyToOne
#JoinColumns({
#JoinColumn(name = "state_id", referencedColumnName="id", columnDefinition = "uuid", insertable = false, updatable = false),
#JoinColumn(name = "site_id", referencedColumnName="site_id", columnDefinition = "uuid", insertable = false, updatable = false)
})
#Type(type = "pg-id-uuid")
private ActivityState state;**
#ManyToOne
#JoinColumns({
#JoinColumn(name = "activity_type_id", referencedColumnName="id", columnDefinition = "uuid", insertable = false, updatable = false),
#JoinColumn(name = "site_id", referencedColumnName="site_id", columnDefinition = "uuid", insertable = false, updatable = false)
})
#Type(type = "pg-id-uuid")
#OnDelete(action = OnDeleteAction.CASCADE)
private ActivityType activityType;
public UUID getId(){
return this.getCommonId().getId();
}
public void setId(UUID id){
if (this.getCommonId() == null) {
this.setCommonId(new CommonId());
}
this.getCommonId().setId(id);
}
public void setSite(Site site){
this.site = site;
if (this.getCommonId() == null) {
this.setCommonId(new CommonId());
}
this.getCommonId().setSiteId(site.getId());
}
}
The setId/getId/ setSite are overriden in order to update the entity when using a mapper to convert from the DTO to the Entity
The ActivityState is as follows:
#Entity
#Table(name = "activity_states", schema = "ptw")
#Data
#TypeDefs({
#TypeDef(name = "pg-id-uuid", typeClass = PostgresIdUUIDType.class)
})
public class ActivityState extends AuditAtBy implements Serializable {
#EmbeddedId
private CommonId commonId;
#MapsId("siteId")
#ManyToOne
#OnDelete(action = OnDeleteAction.CASCADE)
private Site site;
#Column(nullable = false, length = 50)
private String name;
#Column(name = "icon_id", nullable = false)
private short iconId;
#Column(name = "is_initial", nullable = false)
private boolean isInitial;
#Column(name = "order_number", nullable = false)
private short orderNumber;
public UUID getId(){
return this.getCommonId().getId();
}
public void setId(UUID id){
if (this.getCommonId() == null) {
this.setCommonId(new CommonId());
}
this.getCommonId().setId(id);
}
public void setSite(Site site){
this.site = site;
if (this.getCommonId() == null) {
this.setCommonId(new CommonId());
}
this.getCommonId().setSiteId(site.getId());
}
}
When I try to save the exception is:
Caused by: org.postgresql.util.PSQLException: ERROR: null value in column "state_id" violates not-null constraint
Detail: Failing row contains (c821ff72-de93-4c03-abf5-e18347c29955, null, 0, 5081790f-19ed-44e0-be17-94f94aed878b, null, null, test, test, null, 1, 1, f, N/A, 1, null, null, null, null, null, null, null, null, null, null, null, f, {"title": "test", "DateTable": {"timeTo": "", "timeFrom": "", "v..., 2021-12-09 13:46:02.829157+01, 2021-12-09 13:46:02.829157+01, 7b0702c7-9f11-4a92-bfdf-7f98eb8ac94d, 7b0702c7-9f11-4a92-bfdf-7f98eb8ac94d, null, null, f).
I have no idea about how to solve although I have tried multiple things changing the mappings and relationships.
I know that composites keys are not the best approach but we have a multitenancy system where the best approach to keep data isolated was this one.
The answer was basically the first answer to this post: Should Hibernate be able to handle overlapping foreign keys?
Basically changing the mapping to be with the actual column as column and the one repeated in other entities as formula it is working now:
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(column = #JoinColumn(name="state_id", referencedColumnName = "id")),
#JoinColumnOrFormula(formula = #JoinFormula(value="site_id", referencedColumnName = "site_id")) })
Related
I have a couple of tables with relation as in the image below
I created hibernate data model as follows
#Entity
#Table(name = "SUBJECT")
public class Subject {
#Column(name = "NAME")
private String name;
#Column(name = "ADDRESS")
private String address;
#Column(name = "CLIENT_ID")
private String clientId;
#OneToMany(mappedBy = "subject", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<SSI> SSIs;
// getters and setters
...
}
#Entity
#Table(name = "SUBJECT_IDENTIFIER")
public class SubjectIdentifier {
#Column(name = "VALUE")
private String value;
#Column(name = "AUTHORITY")
private String authority;
#Column(name = "TYPE")
private String type;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "SUBJECT_ID", referencedColumnName = "ID", insertable = true,
updatable = true,
#JoinColumn(name = "CLIENT_ID", referencedColumnName = "CLIENT_ID", insertable =
true, updatable = true)
})
private Subject subject;
// getters and setters
...
}
#Entity
#Table(name = "SSI")
public class SSI {
#ManyToOne
#JoinColumns({
#JoinColumn(name = "SUBJECT_ID", referencedColumnName = "ID", insertable = true,
updatable = true),
#JoinColumn(name = "CLIENT_ID", referencedColumnName = "CLIENT_ID", insertable =
true, updatable = true)
})
private Subject subject;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "SUBJECT_IDENTIFIER_ID", referencedColumnName = "ID", insertable = true,
updatable = true),
#JoinColumn(name = "CLIENT_ID", referencedColumnName = "CLIENT_ID", insertable =
true, updatable = true)
})
private SubjectIdentifier subjectIdentifier;
// getters and setters
...
}
I intend to create the entities as follows
...
Subject s = new Subject();
//.. initialization of s goes here
SubjectIdentifier si = new SubjectIdentifier();
//.. initialization of si goes here
SSI ssi = new SSI();
ssi.setSubject(s);
ssi.setSubjectIdentifier(si);
s.setSSI(ssi);
...
emProvider.get().persist(s);
When I run this, I get following error
org.hibernate.MappingException: Repeated column in mapping for entity: *.SSI column: CLIENT_ID (should be mapped with insert="false" update="false")
If I set insert="false" update="false" for CLIENT_ID, it would error again about mixing of insert & update with other column in the #Joincolumns
If I set insert="false" update="false" for all the #JoinColumns then it will not persist the objects.
How to really handle this kind of entity creation?
That's not so easy. If you want that, you have to introduce another attribute for storing the client id and maintain this denormalization:
#Entity
#Table(name = "SSI")
public class SSI {
#Column(name = "CLIENT_ID")
private String clientId;
#Column(name = "SUBJECT_ID")
private String subjectId;
#Column(name = "SUBJECT_IDENTIFIER_ID")
private String subjectIdentifierId;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "SUBJECT_ID", referencedColumnName = "ID", insertable = false,
updatable = false),
#JoinColumn(name = "CLIENT_ID", referencedColumnName = "CLIENT_ID", insertable =
false, updatable = false)
})
private Subject subject;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "SUBJECT_IDENTIFIER_ID", referencedColumnName = "ID", insertable = false,
updatable = false),
#JoinColumn(name = "CLIENT_ID", referencedColumnName = "CLIENT_ID", insertable =
false, updatable = false)
})
private SubjectIdentifier subjectIdentifier;
// getters and setters
...
}
I'm having issues with the next samples whenever I remove the one to one relationship from the CustomerOrderEntity my tests are passed without problems however when I put back the relationship it fails telling me that a String can't be assigned to an Integer
What do I have wrong in my code?
How can I solve it?
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Accessors(chain = true)
#Table(name = "cust_order")
#EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
#NamedEntityGraphs({
#NamedEntityGraph(
name = CustomerOrderEntity.GRAPH_WITH_LINES,
attributeNodes = {
#NamedAttributeNode(value = "lines", subgraph = "subgraph.alternatives")
},
subgraphs = {
#NamedSubgraph(
name = "subgraph.alternatives",
attributeNodes = #NamedAttributeNode(value = "alternatives")
)
}
),
#NamedEntityGraph(
name = CustomerOrderEntity.GRAPH_WITH_DELIVERY,
attributeNodes = {
#NamedAttributeNode(value = "delivery")
}
),
#NamedEntityGraph(
name = CustomerOrderEntity.GRAPH_WITH_ALL_PROPS,
attributeNodes = {
#NamedAttributeNode(value = "lines", subgraph = "subgraph.alternatives"),
#NamedAttributeNode("delivery")
},
subgraphs = {
#NamedSubgraph(
name = "subgraph.alternatives",
attributeNodes = #NamedAttributeNode(value = "alternatives")
)
}
)
})
public class CustomerOrderEntity extends Auditable implements Serializable {
private static final long serialVersionUID = 1L;
public static final String GRAPH_WITH_LINES = "graph.CustomerOrderEntity.lines";
public static final String GRAPH_WITH_DELIVERY = "graph.CustomerOrderEntity.delivery";
public static final String GRAPH_WITH_ALL_PROPS = "graph.CustomerOrderEntity.all";
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "hash_generator")
#GenericGenerator(
name = "hash_generator",
strategy = "com.persistence.generator.HashSequenceGenerator",
parameters = {
#org.hibernate.annotations.Parameter(name = HashSequenceGenerator.EXTRACT_FIELDS_HASHING, value = "tenantId,fulfilLocationId,fulfilOrderId"),
#org.hibernate.annotations.Parameter(name = HashSequenceGenerator.EXTRACT_FIELDS_FORMAT, value = HashSequenceGenerator.EXTRACT_FIELDS_FORMAT_HEX_VALUE)
}
)
#Column(name = "cust_order_key", nullable = false)
private String custOrderKey;
#Column(name = "tenant_id")
private Integer tenantId;
#Column(name = "fulfil_location_id")
private Integer fulfilLocationId;
#Column(name = "fulfil_order_id")
private String fulfilOrderId;
#OneToMany(
targetEntity = CustomerOrderLineEntity.class,
mappedBy = "order",
fetch = FetchType.LAZY
)
#OrderColumn(name = "fulfil_order_line_id")
private List<CustomerOrderLineEntity> lines;
#OneToOne(
targetEntity = CustomerDeliveryEntity.class,
mappedBy = "order",
fetch = FetchType.LAZY
)
private CustomerDeliveryEntity delivery;
}
#Entity
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Accessors(chain = true)
#Table(name = "cust_order_line")
#ToString(onlyExplicitlyIncluded = true)
#EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
public class CustomerOrderLineEntity extends Auditable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "hash_generator")
#GenericGenerator(
name = "hash_generator",
strategy = "com.persistence.generator.HashSequenceGenerator",
parameters = {
#org.hibernate.annotations.Parameter(name = HashSequenceGenerator.EXTRACT_FIELDS_HASHING, value = "tenantId,fulfilLocationId,fulfilOrderId,fulfilOrderLineId"),
#org.hibernate.annotations.Parameter(name = HashSequenceGenerator.EXTRACT_FIELDS_FORMAT, value = HashSequenceGenerator.EXTRACT_FIELDS_FORMAT_HEX_VALUE)
}
)
#Column(name = "cust_order_line_key", nullable = false)
#ToString.Include
#EqualsAndHashCode.Include
private String custOrderLineKey;
#Column(name = "tenant_id")
#ToString.Include
#EqualsAndHashCode.Include
private Integer tenantId;
#Column(name = "fulfil_location_id")
#ToString.Include
#EqualsAndHashCode.Include
private Integer fulfilLocationId;
#Column(name = "fulfil_order_id")
#ToString.Include
#EqualsAndHashCode.Include
private String fulfilOrderId;
#Column(name = "fulfil_order_line_id")
#ToString.Include
#EqualsAndHashCode.Include
private Integer fulfilOrderLineId;
#ManyToOne(
targetEntity = CustomerOrderEntity.class,
fetch = FetchType.LAZY
)
#JoinColumns({
#JoinColumn(name = "tenant_id", referencedColumnName = "tenant_id", updatable = false, insertable = false),
#JoinColumn(name = "fulfil_location_id", referencedColumnName = "fulfil_location_id", updatable = false, insertable = false),
#JoinColumn(name = "fulfil_order_id", referencedColumnName = "fulfil_order_id", updatable = false, insertable = false)
})
private CustomerOrderEntity order;
#OneToMany(
targetEntity = CustomerOrderLineProdAltEntity.class,
mappedBy = "line",
fetch = FetchType.LAZY
)
private List<CustomerOrderLineProdAltEntity> alternatives;
}
#Entity
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Accessors(chain = true)
#Table(name = "order_line_prod_alt")
#ToString(callSuper = true, onlyExplicitlyIncluded = true)
#EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
public class CustomerOrderLineProdAltEntity extends Auditable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "order_line_prod_alt_key", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "hash_generator")
#GenericGenerator(
name = "hash_generator",
strategy = "com.persistence.generator.HashSequenceGenerator",
parameters = {
#org.hibernate.annotations.Parameter(name = HashSequenceGenerator.EXTRACT_FIELDS_HASHING, value = "tenantId,fulfilLocationId,fulfilOrderId,fulfilOrderLineId,altGtin"),
#org.hibernate.annotations.Parameter(name = HashSequenceGenerator.EXTRACT_FIELDS_FORMAT, value = HashSequenceGenerator.EXTRACT_FIELDS_FORMAT_HEX_VALUE)
}
)
#ToString.Include
#EqualsAndHashCode.Include
private String orderLineProdAltKey;
#Column(name = "tenant_id")
#ToString.Include
#EqualsAndHashCode.Include
private Integer tenantId;
#Column(name = "fulfil_location_id")
#ToString.Include
#EqualsAndHashCode.Include
private Integer fulfilLocationId;
#Column(name = "fulfil_order_id")
#ToString.Include
#EqualsAndHashCode.Include
private String fulfilOrderId;
#Column(name = "fulfil_order_line_id")
#ToString.Include
#EqualsAndHashCode.Include
private Integer fulfilOrderLineId;
#ManyToOne(
targetEntity = CustomerOrderLineEntity.class,
fetch = FetchType.LAZY
)
#JoinColumns({
#JoinColumn(name = "tenant_id", referencedColumnName = "tenant_id", updatable = false, insertable = false),
#JoinColumn(name = "fulfil_location_id", referencedColumnName = "fulfil_location_id", updatable = false, insertable = false),
#JoinColumn(name = "fulfil_order_id", referencedColumnName = "fulfil_order_id", updatable = false, insertable = false),
#JoinColumn(name = "fulfil_order_line_id", referencedColumnName = "fulfil_order_line_id", updatable = false, insertable = false)
})
private CustomerOrderLineEntity line;
}
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Accessors(chain = true)
#Table(name = "cust_delivery")
#EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
public class CustomerDeliveryEntity extends Auditable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "hash_generator")
#GenericGenerator(
name = "hash_generator",
strategy = "com.persistence.generator.HashSequenceGenerator",
parameters = {
#org.hibernate.annotations.Parameter(name = HashSequenceGenerator.EXTRACT_FIELDS_HASHING, value = "tenantId,fulfilLocationId,fulfilOrderId"),
#org.hibernate.annotations.Parameter(name = HashSequenceGenerator.EXTRACT_FIELDS_FORMAT, value = HashSequenceGenerator.EXTRACT_FIELDS_FORMAT_HEX_VALUE)
}
)
#Column(name = "cust_delivery_key", nullable = false)
private String custDeliveryKey;
#Column(name = "tenant_id")
private Integer tenantId;
#Column(name = "fulfil_location_id")
private Integer fulfilLocationId;
#Column(name = "fulfil_order_id")
private String fulfilOrderId;
#OneToOne(
targetEntity = CustomerOrderEntity.class,
fetch = FetchType.LAZY
)
#JoinColumns({
#JoinColumn(name = "tenant_id", referencedColumnName = "tenant_id", updatable = false, insertable = false),
#JoinColumn(name = "fulfil_location_id", referencedColumnName = "fulfil_location_id", updatable = false, insertable = false),
#JoinColumn(name = "fulfil_order_id", referencedColumnName = "fulfil_order_id", updatable = false, insertable = false)
})
private CustomerOrderEntity order;
}
with the next spring boot repository
#Repository
public interface CustomerOrderEntityRepository extends JpaRepository<CustomerOrderEntity, String>, JpaSpecificationExecutor<CustomerOrderEntity> {
#EntityGraph(CustomerOrderEntity.GRAPH_WITH_LINES)
#Query("select entity from CustomerOrderEntity entity where entity.tenantId = :#{#param.tenantId} and entity.fulfilLocationId = :#{#param.fulfilLocationId} and entity.fulfilOrderId = :#{#param.fulfilOrderId}")
Optional<CustomerOrderEntity> findOneByOrderIdWithLines(CustomerOrderEntity param);
#EntityGraph(CustomerOrderEntity.GRAPH_WITH_DELIVERY)
#Query("select entity from CustomerOrderEntity entity where entity.tenantId = :#{#param.tenantId} and entity.fulfilLocationId = :#{#param.fulfilLocationId} and entity.fulfilOrderId = :#{#param.fulfilOrderId}")
Optional<CustomerOrderEntity> findOneByOrderIdWithDelivery(CustomerOrderEntity param);
#EntityGraph(CustomerOrderEntity.GRAPH_WITH_ALL_PROPS)
#Query("select entity from CustomerOrderEntity entity where entity.tenantId = :#{#param.tenantId} and entity.fulfilLocationId = :#{#param.fulfilLocationId} and entity.fulfilOrderId = :#{#param.fulfilOrderId}")
Optional<CustomerOrderEntity> findOneByOrderIdWithAllProps(CustomerOrderEntity param);
}
and the next test:
TestCase
And when I try to retrieve a CustomerOrderEntity from the repository it gives the next error trace:
Stacktrace
I think you need to add getters and setters for the properties of your domain objects. You might start by adding a getter and a setter for CustomerOrderEntity.tenantId to see if that changes the behaviour when you try to reproduce this issue.
Need criteria join query for a composite primary key.
Entities:
ArtWork
#Entity
#Table(name = "artwork")
public class ArtWork implements io.malevich.web.entity.Entity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Id
#Column(name = "language", columnDefinition = "CHAR(2)")
private String language;
#Column(name = "art_name", nullable = false)
private String artName;
#Column(name = "creation_date", nullable = false)
private Date creationDate;
#Column(name = "edition_flag", nullable = false, columnDefinition = "tinyint(1)")
private boolean editionFlag;
#Column(name = "replica_flag", nullable = false, columnDefinition = "tinyint(1)")
private boolean replicaFlag;
#Column(name = "number_of_editions")
private Long numberOfEditions;
#Column(name = "original_id")
private Long originalId;
#ManyToOne
#JoinColumns({
#JoinColumn(
name = "category_id",
referencedColumnName = "id", insertable = false, updatable = false),
#JoinColumn(
name = "language",
referencedColumnName = "language", insertable = false, updatable = false)
})
private Category category;
#ManyToOne
#JoinColumns({
#JoinColumn(
name = "gallery_id",
referencedColumnName = "id", insertable = false, updatable = false),
#JoinColumn(
name = "language",
referencedColumnName = "language", insertable = false, updatable = false)
})
private Gallery gallery;
#ManyToOne
private Specialization specialization;
#ManyToOne
#JoinColumns({
#JoinColumn(
name = "author_id",
referencedColumnName = "id", insertable = false, updatable = false),
#JoinColumn(
name = "language",
referencedColumnName = "language", insertable = false, updatable = false)
})
private Author author;
#Column
private String description;
#Column
private Double price;
//getter setter
}
User:
#javax.persistence.Entity
#Table(name = "user")
public class User implements Entity, UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true, length = 255, nullable = false)
private String name;
#Column(length = 255, nullable = false)
private String password;
#ElementCollection(fetch = FetchType.EAGER)
private Set<Role> roles = new HashSet<>();
#Column(name = "user_type_id")
private Long userTypeId;
#ManyToOne
#JoinColumn(name = "person_id", referencedColumnName = "id")
private Person person;
#ManyToOne
#JoinColumn(name = "organization_id", referencedColumnName = "id")
private Organization organization;
#ManyToOne
#JoinColumn(name = "file_id", referencedColumnName = "id")
private File file;
#Column(name = "activity_flag")
private boolean activityFlag;
//gettter and setter
}
Account States
#javax.persistence.Entity
#Table(name = "account_states")
public class AccountStates implements Entity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(insertable = false, updatable = false)
private String language;
#ManyToOne
#JoinColumns({ #JoinColumn(name = "artwork_id", referencedColumnName = "id"),
#JoinColumn(name = "language", referencedColumnName = "language") })
private ArtWork artwork;
#ManyToOne
#JoinColumn(name = "art_owner_id", referencedColumnName = "id")
private User artOwner;
#Column(name = "quantity")
private Long quantity;
#Temporal(TemporalType.DATE)
#Column(name = "buy_date")
private Date buyDate;
}
Account State Dao:
public class JpaAccountStatesDao extends JpaDao
implements AccountStatesDao {
public JpaAccountStatesDao() {
super(AccountStates.class);
}
#Override
public AccountStates find(Long artOwnerId, Long artworkId, String language) {
final CriteriaBuilder builder = this.getEntityManager().getCriteriaBuilder();
final CriteriaQuery<AccountStates> criteriaQuery = builder.createQuery(AccountStates.class);
Root<AccountStates> root = criteriaQuery.from(AccountStates.class);
Predicate p1 = builder.and(builder.equal(root.get("artwork"), artworkId),
builder.equal(root.get("artwork"), language), builder.equal(root.get("artOwner"), artOwnerId));
criteriaQuery.where(p1);
TypedQuery<AccountStates> typedQuery = this.getEntityManager().createQuery(criteriaQuery);
return typedQuery.getSingleResult();
}
}
I want to find Account States where artOwner id = 1 and language = en and artwork id = 1.
Can anyone suggest proper query for the same?
I found a solution for the same, I tried to pass a whole object instead of object id.
So final query is:
#Override
public AccountStates find(User artOwner, Artwork artwork) {
final CriteriaBuilder builder = this.getEntityManager().getCriteriaBuilder();
final CriteriaQuery<AccountStates> criteriaQuery = builder.createQuery(AccountStates.class);
Root<AccountStates> root = criteriaQuery.from(AccountStates.class);
Predicate p1 = builder.and(builder.equal(root.get("artwork"), artwork),
builder.equal(root.get("artOwner"), artOwner));
criteriaQuery.where(p1);
TypedQuery<AccountStates> typedQuery = this.getEntityManager().createQuery(criteriaQuery);
return typedQuery.getSingleResult();
}
}
Now, It works successfully... thanks
I have a following example I want to create a lazy #ManyToOne relation between Car and CarAchievement tables using multiple join columns (example of class below)
#Entity
#Table(name = "CAR")
public class Car implements Serializable {
#Id
#GenericGenerator(name = "SEQ_CAR", strategy = "sequence",
parameters = {
#org.hibernate.annotations.Parameter(
name = "sequence",
value = "SEQ_CAR"
)
}
)
#GeneratedValue(generator = "SEQ_CAR")
#Column(name = "ID")
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumns({
#JoinColumn(name = "region", referencedColumnName = "region", updatable = false, insertable = false),
#JoinColumn(name = "model", referencedColumnName = "model", updatable = false, insertable = false),
#JoinColumn(name = "year", referencedColumnName = "type", updatable = false, insertable = false),
#JoinColumn(name = "type", referencedColumnName = "year", updatable = false, insertable = false)
})
#JsonIgnore
private CarAchievement carAchievement;
}
This relation works fine but it seems not to be a LazyFetch, every query for a CAR seems to be fetching CarAchievement automatically even when its not specified to fetch this relation
Hibernate version: 4.3.10.Final
Ok, so I'd like to implement a simple forum example. So, I have threads, messages and users, of course and these are the pojos (I omitted the usually getters and simplicity)
Message
#Entity
#Table(name = "message")
public class Message implements java.io.Serializable, RecognizedServerEntities
{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#Cascade({ CascadeType.SAVE_UPDATE })
#JoinColumn(name = "thread", nullable = false)
private Thread thread;
#ManyToOne(fetch = FetchType.LAZY)
#Cascade({ CascadeType.SAVE_UPDATE })
#JoinColumn(name = "author", nullable = true)
private User user;
#Column(name = "title", nullable = false, length = 31)
private String title;
#Column(name = "body", nullable = false, columnDefinition = "Text")
private String body;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "last_modified_date", nullable = false, length = 19)
private Date lastModifiedDate;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_date", nullable = false, updatable = false, length = 19)
private Date createdDate;
}
User
#Entity
#Table(name = "user", uniqueConstraints =
{ #UniqueConstraint(columnNames = "email"),
#UniqueConstraint(columnNames = "nick") })
public class User implements java.io.Serializable, RecognizedServerEntities
{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#Column(name = "email", unique = true, nullable = false, length = 31)
private String email;
#Column(name = "password", nullable = false, length = 31)
private String password;
#Column(name = "nick", unique = true, nullable = false, length = 31)
#NaturalId(mutable = false)
private String nick;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "registered_date", nullable = false, updatable = false, length = 19)
private Date registeredDate;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = false)
private Set<Thread> threads = new HashSet<Thread>(0);
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = false)
private /**transient /**/ Set<Message> messages = new HashSet<Message>(0);
}
Thread
#Entity
#Table(name = "thread")
public class Thread implements java.io.Serializable, RecognizedServerEntities
{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#Cascade({CascadeType.SAVE_UPDATE})
#JoinColumn(name = "parent_thread", nullable = true)
private Thread parentThread;
#ManyToOne(fetch = FetchType.LAZY)
#Cascade({CascadeType.SAVE_UPDATE})
#JoinColumn(name = "author", nullable = true)
private User user;
#Column(name = "title", nullable = false, length = 63)
private String title;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "last_modified_date", nullable = false, length = 19)
private Date lastModifiedDate;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_date", nullable = false, updatable = false, length = 19)
private Date createdDate;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "thread"/**/, orphanRemoval = true/**/)
#Cascade({ CascadeType.REMOVE })
private /**transient /**/ Set<Message> messages = new HashSet<Message>(0);
#OneToMany(fetch = FetchType.LAZY, mappedBy = "parentThread", orphanRemoval = true)
#Cascade({CascadeType.REMOVE })
private /**transient /**/ Set<Thread> subThreads = new HashSet<Thread>(0);
}
I have many doubts on the annotations of course, but these are the relevant choice.
When I delete an user, I don't want to delete all his threads and messages, so it make sense to don't use orphan-removal or cascade delete on the #OneToMany associations (ie the messages and threads collections).
Also, because the id is automatically generated from the database, I don't think it make sense at all to use the annotation CascadeType.UPDATE (or SAVE_UPDATE) on the collections of all the entity.
A thread are the most problematic entity to manage. When we delete a thread, we want that all its subthreads and all its messages were deleted. So, I use the CascadeType.REMOVE and orphan-removal annotations.
An all the #ManyToOne associations, I use the CascadeType.ALL. The idea is that if we delete a message or a subthread, all the parents will be updated.
All the collections are not transient.
Feel free to propose suggestion on this of course.
Btw, given the whole story, this is the question: suppose I have a thread "mThread" started from the user "mUser" with many messages from different users, how can I safely delete the user?
I tried different things, but I'm not sure of anything and in most cases I only have exceptions.
EDIT
I also have another class, StorageManager<T>, that is used to encapsulate the common code between entities. Basically, it implements the "one session per transaction" pattern. So each methodX() basically:
invoke sessionFactory.openSession() and session.beginTransaction()
invoke session.methodX()
invoke transaction.commit()
invoke session.clear() and session.close
Example with code
for (Thread t : mUser.getThreads())
{
t.setUser(null);
storageManagerThread.update(t);
}
for (Message m : mUser.getMessages())
{
m.setUser(null);
storageManagerMessage.update(t);
}
storageManagerUser.delete(mUser);
Until this point, all the table in the database have the right values. However, I don't know if it is the right way to proceed, because it leaves dirty collections.
Indeed, when at later point I try to execute some other options (e.g. update(mThread) or delete a message from mThread) a NullPointerException was thrown. Why is this? .