Hibernate - Subclass has to be binded after it's mother class - java

after upgrading hibernate to from version 4.3.7.Final to 5.3.18.Final I got the error below
#Entity
#Audited
#AuditPermission(Permission.VIEW_INDIVIDUAL)
public class Individual implements ITemporalEntity {
#Id
#Column(name = "Individual_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Individual_generator")
#SequenceGenerator(name = "Individual_generator", initialValue = 1, allocationSize = 1, sequenceName = "Individual_id_seq")
private Long id;
#Embedded
private TemporalEntity temporal = new TemporalEntity();
#Override
public DateTime getCreateDate() {
return temporal.getCreateDate();
}
#Override
public void setCreateDate(DateTime createDate) {
temporal.setCreateDate(createDate);
}
.......
...
}
TemporalEntity class
#Embeddable
public class TemporalEntity {
#Column(updatable = false)
private DateTime createDate;
#Column
private DateTime lastModifiedDate;
#ManyToOne
#JoinColumn(name = "created_by_id", updatable = false)
private AdminUser createdBy;
#ManyToOne
#JoinColumn(name = "last_modified_by_id")
private AdminUser lastModifiedBy;
#Column(nullable = false, columnDefinition = "boolean not null default false")
private boolean deleted = false;
public DateTime getCreateDate() {
return createDate;
}
public void setCreateDate(DateTime createDate) {
if (createDate == null) {
//ignore attempts to clear this field
return;
//throw new IllegalStateException("Null create date not allowed");
}
this.createDate = createDate;
}
public DateTime getLastModifiedDate() {
return lastModifiedDate;
}
public void setLastModifiedDate(DateTime lastModifiedDate) {
if (lastModifiedDate == null) {
//ignore attempts to clear this field
return;
//throw new IllegalStateException("Null last modified date not allowed");
}
this.lastModifiedDate = lastModifiedDate;
}
public AdminUser getCreatedBy() {
return createdBy;
}
public void setCreatedBy(AdminUser createdBy) {
if (createdBy == null) {
//ignore attempts to clear this field
return;
//throw new IllegalStateException("Null created by not allowed");
}
this.createdBy = createdBy;
}
public AdminUser getLastModifiedBy() {
return lastModifiedBy;
}
public void setLastModifiedBy(AdminUser lastModifiedBy) {
if (lastModifiedBy == null) {
//ignore attempts to clear this field
return;
//throw new IllegalStateException("Null lastModifiedBy not allowed");
}
this.lastModifiedBy = lastModifiedBy;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}
ITemporalEntity interface
public interface ITemporalEntity {
public DateTime getCreateDate();
public void setCreateDate(DateTime createDate);
public DateTime getLastModifiedDate();
public void setLastModifiedDate(DateTime lastModifiedDate);
public AdminUser getCreatedBy();
public void setCreatedBy(AdminUser createdBy);
public AdminUser getLastModifiedBy();
public void setLastModifiedBy(AdminUser lastModifiedBy);
public boolean isDeleted();
public void setDeleted(boolean deleted);
}
the error stack
an assertion failure occurred (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: Subclass has to be binded after it's mother class: com.berwick.dal.TemporalEntity
23:11:29,486 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 87) MSC000001: Failed to start service jboss.persistenceunit."bds-core-1.0-SNAPSHOT.war#com.berwick.dal": org.jboss.msc.service.StartException in service jboss.persistenceunit."bds-core-1.0-SNAPSHOT.war#com.berwick.dal": org.hibernate.AssertionFailure: Subclass has to be binded after it's mother class: com.berwick.dal.TemporalEntity
at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:198) [wildfly-jpa-21.0.0.Final.jar:21.0.0.Final]
My tries to fix the issue
add #MappedSuperclass to TemporalEntity class
that makes this error gone but I got more errors
Duplicate generator name Individual_generator you will likely want to set the property hibernate.jpa.compliance.global_id_generators to false
at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:198) [wildfly-jpa-21.0.0.Final.jar:21.0.0.Final]

The question is quite similar asked in linked thread.
Error Mapping Embedded class exposed through an interface
write your entity class name that you want to embed in the #target annotation
#Embedded
#Target(TemporalEntity.class)
private ITemporalEntity temporal;
you do not need to tightly couple the TemporalEntity by creating new object embedded do it

Related

How to use Spring Domain Example + Spring Domain Auditable

I have the following issue using Spring Data Domain Example with an entity that implements Spring Data Domain Auditable.
Error :
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [Optional.empty] did not match expected type [java.time.LocalDateTime (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [Optional.empty] did not match expected type [java.time.LocalDateTime (n/a)]
Entity :
import org.springframework.data.domain.Auditable;
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "my_table")
#Builder
public class MyEntity implements Serializable, Auditable<String, Integer, LocalDateTime>
#Getter
#Setter
#Id
#Column(name = "my_table_id", nullable = false)
private Integer id;
// Some fields
// ...
// AUDIT
#Column(name = "my_table_created_by")
#CreatedBy
private String createdBy;
#Column(name = "my_table_created_date")
#CreatedDate
#Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentLocalDateTime")
private LocalDateTime createdDate;
#Column(name = "my_table_last_modified_by")
#LastModifiedBy
private String lastModifiedBy;
#Column(name = "my_table_last_modified_date")
#LastModifiedDate
#Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentLocalDateTime")
private LocalDateTime lastModifiedDate;
#Override
public boolean isNew()
{
return id == null;
}
#NotNull
#Override
public Optional<String> getCreatedBy()
{
return Optional.ofNullable(createdBy);
}
#Override
public void setCreatedBy(#NotNull String createdBy)
{
this.createdBy = createdBy;
}
#NotNull
#Override
public Optional<LocalDateTime> getCreatedDate()
{
return Optional.ofNullable(createdDate);
}
#Override
public void setCreatedDate(
#NotNull
LocalDateTime creationDate)
{
this.createdDate = creationDate;
}
#NotNull
#Override
public Optional<String> getLastModifiedBy()
{
return Optional.ofNullable(lastModifiedBy);
}
#Override
public void setLastModifiedBy(#NotNull String lastModifiedBy)
{
this.lastModifiedBy = lastModifiedBy;
}
#NotNull
#Override
public Optional<LocalDateTime> getLastModifiedDate()
{
return Optional.ofNullable(lastModifiedDate);
}
#Override
public void setLastModifiedDate(#NotNull LocalDateTime lastModifiedDate)
{
this.lastModifiedDate = lastModifiedDate;
}
}
Test :
import org.springframework.data.domain.Example;
#Test
public void test()
{
MyEntity entity = MyEntity.builder()
.someField("someValue")
.build();
List<MyEntity> entities = repository.findAll(Example.of(entity));
}
I guess it has to do with the Optional Methods that I have with Auditable, but I don't see how to fix this problem.
It works if I duplicate the entity class without Auditable but it's not clean.
Fix, I don't need to implement Auditable since I already use #EntityListeners(AuditingEntityListener.class).

PostgreSQL not doing autoincrement

I am using postgresql with springboot. So whenever I am using post method to add a new detail in my table instead of autoincrementing id it's going from 1 to 3. It's taking alternate values rather than consecutive values. I have given following properties and then created table:
spring.jpa.hibernate.ddl-auto=create
Didn't create the table manually. What is the reason for this error? This is my entity class.
#Entity
#Table(name = "NORTHBOUND_SUBSCRIPTION")
public class NBSubscription {
#Id
#GeneratedValue
#Column(name = "nb_id")
private Long nbId;
#Column(name = "DEVICE_FILTER")
private String deviceFilter;
#Column(name = "INTERFACE_FILTER")
private String interfaceFilter;
#ManyToOne
#JoinColumn(name="subscriber_id", referencedColumnName="SUBSCRIBER_ID")
private Subscriber subscriber;
#OneToOne
#JoinColumn(name="sensor_group_id", referencedColumnName="ID")
private SensorGroup sensorGroup;
#Column(name = "EVENT_TYPE")
private String eventType;
#Column(name = "SAMPLING_INTERVAL")
private Integer samplingInterval;
#Column(name = "CREATEAT")
#DateTimeFormat(pattern = "dd-MM-yyyy HH:mm")
private Timestamp createAt;
#Column(name = "MODIFIEDAT")
#DateTimeFormat(pattern = "dd-MM-yyyy HH:mm")
private Timestamp modifiedAt;
#Column(name = "CREATEDBY")
private String createdBy;
#Column(name = "MODIFIEDBY")
private String modifiedBy;
#Column(name = "mark_deletion")
private String markDeletion;
public NBSubscription() {
super();
}
public NBSubscription(Subscriber subscriber, SensorGroup sensorGroup) {
super();
this.subscriber = subscriber;
this.sensorGroup = sensorGroup;
}
public Long getNbId() {
return nbId;
}
public void setNbId(Long nbId) {
this.nbId = nbId;
}
public String getDeviceFilter() {
return deviceFilter;
}
public void setDeviceFilter(String deviceFilter) {
this.deviceFilter = deviceFilter;
}
public String getInterfaceFilter() {
return interfaceFilter;
}
public void setInterfaceFilter(String interfaceFilter) {
this.interfaceFilter = interfaceFilter;
}
#JsonIgnore
public Subscriber getSubscriber() {
return subscriber;
}
public void setSubscriber(Subscriber subscriber) {
this.subscriber = subscriber;
}
public SensorGroup getSensorGroup() {
return sensorGroup;
}
public void setSensorGroup(SensorGroup sensorGroup) {
this.sensorGroup = sensorGroup;
}
public Integer getSamplingInterval() {
return samplingInterval;
}
public void setSamplingInterval(Integer samplingInterval) {
this.samplingInterval = samplingInterval;
}
public String getEventType() {
return eventType;
}
public void setEventType(String eventType) {
this.eventType = eventType;
}
public Timestamp getCreateAt() {
return createAt;
}
public void setCreateAt(Timestamp createAt) {
this.createAt = createAt;
}
public Timestamp getModifiedAt() {
return modifiedAt;
}
public void setModifiedAt(Timestamp modifiedAt) {
this.modifiedAt = modifiedAt;
}
public String getMarkDeletion() {
return markDeletion;
}
public void setMarkDeletion(String markDeletion) {
this.markDeletion = markDeletion;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public String getModifiedBy() {
return modifiedBy;
}
public void setModifiedBy(String modifiedBy) {
this.modifiedBy = modifiedBy;
}
Try this
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
if it doesn't work, try using the table sequence
#Id
#GeneratedValue(strategy=SEQUENCE, generator="CUST_SEQ")
Your autoincrement is implemented with a sequence, and by default entities share the same sequence, so the autoincrement values get spread across the entities.
You could assign each entity its own sequence. But be aware sequences don't participate in transactions. That means if you have a rollback there will be a break in the numbering. Occasional gaps are not avoidable.
If you are making this sequence visible to users and they expect the numbering to be contiguous, my advice is to not use a sequence for that, and keep the user-visible counter in a field separate from the id. If it is visible to users, then at some point it will need to change, and you can't change ids.

JPA find by Api Key returns null

Im working on a Spring Boot application and the repository method to retrieve by apiKey is returning null.
This is the controller:
#RestController
#RequestMapping("/api/wetlab")
public class WetLabController {
#Autowired
WetLabService wetLabService;
#GetMapping("/getAllWetlabsType")
#PreAuthorize("hasRole('INTERNAL')")
public List<WetLab> getAllWetlabs() {
return wetLabService.getAllWetLabs();
}
#GetMapping("/{apiKey}")
#PreAuthorize("hasRole('INTERNAL')")
public WetLab getByApiKey(#PathVariable UUID apiKey) {
return wetLabService.getByApiKey(apiKey);
}
#ExceptionHandler(DataRetrievalFailureException.class)
void handleNotFound(HttpServletResponse response, Exception e) throws IOException {
response.sendError(HttpStatus.NOT_FOUND.value(), e.getMessage());
}
}
This is the service
#Service
public class WetLabService {
#Autowired
WetLabRepository wetLabRepo;
public List<WetLab> getAllWetLabs() {
List<WetLab> wetLabs = new ArrayList<>();
wetLabRepo.findAll().forEach(wetLabs::add);
return wetLabs;
}
public WetLab getByApiKey(UUID apiKey) {
System.out.println(apiKey);
WetLab wetlabOpt = wetLabRepo.findByApiKey(apiKey);
return wetlabOpt;
// if (wetlabOpt.isPresent()) {
// } else {
// throw new NotFoundException("WetLab not found");
// }
}
}
And this is the repo
#Repository
public interface WetLabRepository extends CrudRepository<WetLab, Long> {
public WetLab findByApiKey(UUID apiKey);
}
And this is the wetlab class
#Entity
#Table(name = "wetlab")
public class WetLab {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO, generator = "wetLab_seq")
#SequenceGenerator(name = "wetLab_seq", sequenceName = "wetLab_seq", allocationSize = 1)
private Long id;
#Column(name = "apiKey", updatable = true, nullable = false, unique = true, columnDefinition = "BINARY(16)")
#NotNull
private UUID apiKey;
#Column(name = "name", length = 50)
#NotNull
private String name;
#OneToMany
private List<Plot> plot;
// Accessors
public WetLab() {
}
public WetLab(Long id, UUID apiKey, String name) {
this.id = id;
this.apiKey = apiKey;
this.name = name;
}
}
The another method is working without errors and the path variable apiKey is not null.
Its strange because in other projects this approach worked fine...
Thanks you

Related entities are null after calling saveAll in Spring JPA

I have these entities
NormalizedChannelStock.java
#Entity
#Table(name = "stocks")
public class NormalizedChannelStock {
#EmbeddedId
private NormalizedChannelStockId id;
#Column(name = "qty")
private int qty;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "channel_id", insertable = false, updatable = false)
private Channel channel;
#Column(name = "created_at", updatable = false)
private Timestamp createdAt;
#Column(name = "updated_at", updatable = false)
private Timestamp updatedAt;
public NormalizedChannelStockId getId() {
return id;
}
public void setId(NormalizedChannelStockId id) {
this.id = id;
}
public int getQty() {
return qty;
}
public void setQty(int qty) {
this.qty = qty;
}
public Channel getChannel() {
return channel;
}
public void setChannel(Channel channel) {
this.channel = channel;
}
public Timestamp getCreatedAt() {
return createdAt;
}
public Timestamp getUpdatedAt() {
return updatedAt;
}
}
NormalizedChannelStockId.java
#Embeddable
public class NormalizedChannelStockId implements Serializable {
#Column(name = "channel_id")
private Integer channelId;
#Column(name = "sku")
private String sku;
public NormalizedChannelStockId() {
}
public NormalizedChannelStockId(Integer channelId, String sku) {
this.channelId = channelId;
this.sku = sku;
}
public Integer getChannelId() {
return channelId;
}
public void setChannelId(Integer channelId) {
this.channelId = channelId;
}
public String getSku() {
return sku;
}
public void setSku(String sku) {
this.sku = sku;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NormalizedChannelStockId that = (NormalizedChannelStockId) o;
return channelId.equals(that.channelId) &&
sku.equals(that.sku);
}
#Override
public int hashCode() {
return Objects.hash(channelId, sku);
}
}
Channel.java
#Entity
#Table(name = "channels")
public class Channel {
#Id
#Column(name = "channel_id")
private int channelId;
#Column(name = "channel_name")
private String channelName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "store_id", insertable = false, updatable = false)
private Store store;
public int getChannelId() {
return channelId;
}
public void setChannelId(int channelId) {
this.channelId = channelId;
}
public String getChannelName() {
return channelName;
}
public void setChannelName(String channelName) {
this.channelName = channelName;
}
public Store getStore() {
return store;
}
public void setStore(Store store) {
this.store = store;
}
}
The problem I'm facing is when I call
List<NormalizedChannelStock> entitiesToSave = ...
List<NormalizedChannelStock> savedEntities = normalizedChannelStockService.saveAll(entitiesToSave);
The returned entities in savedEntities have their Channel inner objects set to null, as well as their created_at and updated_at as shown
Is this normal behaviour? When I run a findAllById on the Repository, the Channels inside the Entities are loaded lazily properly, so I believe the entities are properly mapped in code. The problem is after I save them.
Does JPA not reload the entity after saving it?
As you stated in the comments you did not set those values before saving.
JPA does not load them for you. JPA pretty much doesn't load anything upon saving except the id if it is generated by the database.
A more common case of the same problem/limitation/misconceptions are bidirectional relationships: JPA pretty much ignores the not owning side and the developer has to make sure that both sides are in sync at all times.
You would have to refresh the entity yourself. Note that just loading it in the same transaction would have no effect because it would come from the 1st level cache and would be exactly the same instance.

How do I use update using hibernate where i am not sure on which field is to be altered?

public User updateUser(User user) {
try {
User result = session.get(User.class, user.getId());
if (result == null) {
throw new FilamentNoSuchRecordException(new CoreError(304, "User does not exist"));
}
session.clear();
session.update(user);
return user;
} catch (HibernateException e) {
e.printStackTrace();
}
throw new FilamentDataConnectivityException(new CoreError(305,"Connectivity issue. Please see System Administrator"));
}
customer model is as follows
#Entity
#Table(name = "customers")
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
#DynamicUpdate(value=true)
#SelectBeforeUpdate(value=true)
#SQLDelete(sql="Update customers SET deleted = true where customer_id=?")
#Where(clause="deleted != true")
#ApiModel(description="Create or update Customers")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Customer {
#Id
#Column(name="customer_id")
#NotNull
#GeneratedValue(strategy=GenerationType.AUTO)
private int id = 0;
#Column(name="name")
#ApiModelProperty(value="The name of the customer", example="Mr J. Bond")
#NotNull
private String name;
#Column(name="description")
#ApiModelProperty(value="Desciption of the customer")
#NotNull
private String description;
#Column(name="logo_url")
#ApiModelProperty(value="Logo of user")
#NotNull
private String logo;
#Column(name="created_at")
#ApiModelProperty(value="The date the item was created", example="")
#NotNull
private Date createdAt;
#Column(name="updated_at")
#ApiModelProperty(value="The date the item was updated", example="")
#NotNull
private Date updatedAt;
#ApiModelProperty(hidden=true)
#OneToMany(fetch = FetchType.LAZY, mappedBy = "customer")
private Set<Application> applications = new HashSet<Application>();
#ManyToMany(mappedBy = "customers")
private Set<Service> services = new HashSet<Service>();
#ApiModelProperty(hidden=true)
#OneToMany(fetch = FetchType.LAZY, mappedBy = "customer")
private Set<User> users = new HashSet<User>();
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(
name = "customer_subscription",
joinColumns = #JoinColumn(name = "customer_id"),
inverseJoinColumns = #JoinColumn(name = "subscription_id")
)
private Set<Subscription> subscriptions = new HashSet<Subscription>();
#ApiModelProperty(hidden=true)
#OneToMany(fetch = FetchType.LAZY, mappedBy = "customer")
private Set<Corpus> corpus = new HashSet<Corpus>();
#Column(name="deleted")
#NotNull
private boolean deleteFlag;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Application> getApplications() {
return applications;
}
public void setApplications(Set<Application> applications) {
this.applications = applications;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getLogo() {
return logo;
}
public void setLogo(String logo) {
this.logo = logo;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public Set<Service> getServices() {
return services;
}
public void setServices(Set<Service> services) {
this.services = services;
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
public Set<Corpus> getCorpus() {
return corpus;
}
public void setCorpus(Set<Corpus> corpus) {
this.corpus = corpus;
}
public Set<Subscription> getSubscriptions() {
return subscriptions;
}
public void setSubscriptions(Set<Subscription> subscriptions) {
this.subscriptions = subscriptions;
}
public boolean getDeleteFlag() {
return deleteFlag;
}
public void setDeleteFlag(boolean deleteFlag) {
this.deleteFlag = deleteFlag;
}
}
I check whether the object exists within the database, then update with an object, for example all fields could be null apart from the ID and the one thats needs to be updated. All fields are set to #NotNull in the model and I am using the #DynamicUpdate(value=true) and #SelectBeforeUpdate(value=true) annotations, but these seem to do nothing.
Just get failure saying the null fields can not be null. How do I update the row?
As we discussed in above comments, try this -
public User updateUser(User user) {
try {
User result = session.get(User.class, user.getId());
if (result == null) {
throw new FilamentNoSuchRecordException(new CoreError(304, "User does not exist"));
}
result.setName(user.getName()); // update some properties
session.update(result); // you should update 'result', not 'user'
return result;
} catch (HibernateException e) {
e.printStackTrace();
throw new FilamentDataConnectivityException(new CoreError(305,"Connectivity issue. Please see System Administrator"));
}
}
By using this method I found in another stack overflow post solved the issue. This checks each field and uses the 'not null' value. Then i can update from an object with only 1 field changed.
public static <T> T getNotNull(T a, T b) {
return b != null && a != null && !a.equals(b) ? a : b;
}

Categories

Resources