Strange behavior of just one #OneToOne property after migrating to SpringBoot 3.0 and Hibernate 6.1.5 - spring

After the migration, only one relationship just stopped working for me. Other similar things are working.
So, I have such a relationship. in which, if I get an account with user and billing, user is avialable, and the billing is null.
If I pull out the billing by account id, the billing is there.
//Account
#OneToOne(mappedBy = "account", cascade = CascadeType.ALL)
private User user;
#OneToOne(mappedBy = "account", cascade = CascadeType.ALL)
private Billing billing = new Billing();
//Billing
#OneToOne
#JoinColumn(name = "account_id", nullable = false)
private Account account;
//User
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "account_id")
private Account account;
To get account, such a query is executed. If you run it from the console, then the billing ID is also there.
[Hibernate]
/* <criteria> */ select
a1_0.id,
b1_0.id,
b1_0.account_id,
b1_0.created,
b1_0.last_modified,
a1_0.created,
a1_0.last_modified
from
account a1_0
left join
user u1_0
on a1_0.id=u1_0.account_id
left join
billing b1_0
on a1_0.id=b1_0.account_id
where
u1_0.id=?
What was I trying to do?
I tried to change "Naming strategy" https://thorben-janssen.com/sequence-naming-strategies-in-hibernate-6/
Created a clean database.
Nothing helped.
Who has any ideas? What could be the problem?

#m-deinum here
Entities:
#Entity(name = "account")
#EntityListeners(AuditingEntityListener.class)
public class Account {
#Id
#GeneratedValue
private long id;
#OneToOne(mappedBy = "account", cascade = CascadeType.ALL)
private User user;
#OneToOne(mappedBy = "account", cascade = CascadeType.ALL)
private Billing billing;
#CreatedDate
#Convert(converter = LocalDateTimeConverter.class)
#Column(name = "created", columnDefinition = "DATETIME")
private LocalDateTime created;
#LastModifiedDate
#Convert(converter = LocalDateTimeConverter.class)
#Column(name = "last_modified", columnDefinition = "DATETIME")
private LocalDateTime lastModified;
}
#EntityListeners(AuditingEntityListener.class)
#Entity(name = "user")
public class User {
#Id
#GeneratedValue
private long id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "account_id")
private Account account;
#CreatedDate
#Convert(converter = LocalDateTimeConverter.class)
#Column(name = "created", columnDefinition = "DATETIME")
private LocalDateTime created;
#LastModifiedDate
#Convert(converter = LocalDateTimeConverter.class)
#Column(name = "last_modified", columnDefinition = "DATETIME")
private LocalDateTime lastModified;
}
#Entity(name = "billing")
#EntityListeners(AuditingEntityListener.class)
public class Billing {
#Id
#GeneratedValue
private long id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "account_id")
private Account account;
#CreatedDate
#Convert(converter = LocalDateTimeConverter.class)
#Column(name = "created", columnDefinition = "DATETIME")
private LocalDateTime created;
#LastModifiedDate
#Convert(converter = LocalDateTimeConverter.class)
#Column(name = "last_modified", columnDefinition = "DATETIME")
private LocalDateTime lastModified;
}
I am using Spring Data JPA Repositories. Standard accountRepository.findById(2L) or findAccountByUserId - the same result. User is there but not billing.
public interface AccountRepository extends JpaRepository<Account, Long> {
#EntityGraph(attributePaths = {"billing"})
Optional<Account> findAccountByUserId(long id);
}
Dependencies:
Only SpringBoot Dependencies and ecpecially SB version: 3.0.0. spring-boot-starter-parent and spring-boot-starter-data-jpa(there is hibernate-core:6.1.5)

Related

Spring Data persisting Phantom Child with Null value - not null property references a null or transient value

I have the following Entities in my Project:
#Getter
#Setter
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = { "purchaseId" }))
public class Purchase {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long purchaseId;
#Column(unique = true, nullable = false, length = 15)
private String purchaseNo;
#Column(nullable = false, length = 15)
private String batchCode;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "supplier.supplierId", foreignKey = #ForeignKey(name = "FK_purchase_supplier"), nullable = false)
private Supplier supplier;
#Column(nullable = false)
private LocalDate purchaseDate;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "purchaseId", nullable = false)
private List<PurchaseItem> purchaseItems;
private Double totalAmount;
#ManyToOne
#JoinColumn(name = "userId", nullable = false, foreignKey = #ForeignKey(name = "FK_invoice_purchases"))
private User staff;
#Column(length = 100)
private String remarks;
#Column(nullable = false, updatable = false)
#CreationTimestamp
private LocalDateTime createdAt;
private boolean isDeleted = false;
}
#Getter
#Setter
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = {"purchaseItemId"}))
public class PurchaseItem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long purchaseItemId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "purchaseId", insertable = false, updatable = false, foreignKey = #ForeignKey(name="FK_purchase_item"))
private Purchase purchase;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "productId", foreignKey = #ForeignKey(name="FK_product_item"), nullable = false)
private Product product;
private Double itemAmount;
#Column(nullable = false)
private Double quantity;
private Double itemTotalAmount;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#PrimaryKeyJoinColumn(foreignKey = #ForeignKey(name = "FK_purchacase_item_batch"))
private PurchaseProductBatch productPurchaseBatch;
public void setPurchaseProductBatch() {
PurchaseProductBatch productPurchaseBatch = new PurchaseProductBatch();
productPurchaseBatch.setProduct(this.product);
productPurchaseBatch.setQuantity(this.quantity);
productPurchaseBatch.setPurchaseItem(this);
this.productPurchaseBatch = productPurchaseBatch;
}
}
#Getter
#Setter
#Entity
#Table()
public class PurchaseProductBatch{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long productBatchId;
#ManyToOne(cascade = CascadeType.DETACH)
#JoinColumn(name = "productId", foreignKey = #ForeignKey(name = "FK_product_purch"))
private Product product;
private Double quantity;
#OneToOne(fetch = FetchType.EAGER)
#MapsId
private PurchaseItem purchaseItem;
private boolean isDeleted = false;
#OneToMany(cascade = CascadeType.PERSIST)
#JoinColumn(name = "productBatchId", foreignKey = #ForeignKey(name = "FK_purchase_batch_qty"))
private Set<InvoicePurchaseBatchQuantity> invoicePurchaseBatchQuantities;
}
During Purchase Insert, everything works fine. However, if I update the Purchase record in the database and add new PurchaseItem entry, I encounter the issue below:
org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value : com.be.entity.PurchaseItem.product; nested
I have debugged my application and I see that there is a Product instance inside all of the PurchaseItem. When I commented out the PurchaseProductBatch inside PurchaseItem, everything works fine so I conclude that it is the causing the issue. However, I don't understand how and why JPA seems to create phantom PurchaseItem Records with no value.
Also, if I only update an existing PurchaseItem entry in Purchase, I don't encounter any issues.

Spring context indexer causes issues with hibernate entity mapping

I have a project that is split between multiple modules, each module is imported into the main one as a maven dependency.
Persistence entities can be located at any of the projects but under the same package.
I have been trying to improve the startup time of the application by using the spring-context-indexer but it seems to cause an issue with detecting entities.
My #EntityScan is configured like this:
#EntityScan(basePackages = {"com.botscrew", "com.botscrew.demoadminpanel.entity.jpa","com.botscrew.admin.entity"})
The strange thing is that error looks like this
org.hibernate.AnnotationException: #OneToOne or #ManyToOne on com.botscrew.admin.entity.Bot.amioWhatsAppConfigs references an unknown entity: com.botscrew.admin.entity.services.configs.AmioWhatsAppConfigs
Essentially both entities are located under the same package but Bot entity was resolved but AmioWhatsAppConfigs was not.
The application starts perfectly fine without spring indexer.
I am using spring boot 2.2.1.RELEASE
Entities classes:
#Getter
#Setter
#Builder
#Entity
#ToString(of = {"id", "name"})
#AllArgsConstructor
#Table(name = "admin_bot")
#DiscriminatorValue("Bot")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Bot {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Convert(converter = EmojiConverter.class)
private String name;
private Integer timezone;
private String greetingText;
#Column(columnDefinition = "tinyint(1) default 1")
private Boolean active;
#Column(unique = true, updatable = false, nullable = false)
private String publicIdentifier;
#OneToOne(fetch = FetchType.LAZY)
private PersistentMenuEntity persistentMenuEntity;
//TODO FetchType.LAZY
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "widget_id")
private Widget widget;
//TODO FetchType.LAZY
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private MessengerConfigs messengerConfigs;
//TODO FetchType.LAZY
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private AmioWhatsAppConfigs amioWhatsAppConfigs;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private TwilioConfigs twilioConfigs;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private DialogflowConfigs dialogflowConfigs;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private ChatbaseConfig chatbaseConfig;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private SupportSettings supportSettings;
#OneToMany
private Set<Tag> tags;
#OneToMany(mappedBy = "bot")
private List<Broadcast> broadcasts;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "admin_bot_features",
joinColumns = {#JoinColumn(name = "bot_id")},
inverseJoinColumns = {#JoinColumn(name = "feature_id")})
private Set<Feature> features;
public Bot() {
this.active = true;
}
public Bot(String name, DefaultWidgetProperties defaultWidgetProperties) {
this.publicIdentifier = UUID.randomUUID().toString();
this.chatbaseConfig = new ChatbaseConfig();
this.amioWhatsAppConfigs = new AmioWhatsAppConfigs();
this.timezone = 0;
this.name = name;
this.active = true;
this.messengerConfigs = new MessengerConfigs();
this.dialogflowConfigs = new DialogflowConfigs();
this.widget = new Widget(defaultWidgetProperties);
this.supportSettings = new SupportSettings(false);
}
}
#Getter
#Setter
#Entity
#Accessors(chain = true)
#ToString
#Table(name = "admin_amio_whatsapp_configs")
public class AmioWhatsAppConfigs implements AmioWhatsAppBot {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String channelId;
private String accessToken;
private String secret;
}
Please help
I am editing my answer please check example
#EntityScan(basePackages = {"com.botscrew",
"com.botscrew.demoadminpanel.entity.jpa","com.botscrew.admin.entity.services.configs.*"})

Could not write JSON: Infinite recursion

I am getting StackOverflow recursion error when I run query in Postman or Browser .
When i run says:
.w.s.m.s.DefaultHandlerExceptionResolver : Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError)
Here is the model classes :
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
private String title;
#NotNull
private String description;
#NotNull
private double price;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "category_id", nullable = false)
private Category category;
private boolean isSealed;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "currency_id", nullable = false)
private Currency currency;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "user_id", nullable = false)
private User user;
#Nullable
#OneToMany(mappedBy = "product",
cascade = CascadeType.ALL, orphanRemoval = true)
private List<Images> images;
private Date createdDate = new Date();
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "product")
private View view;
#OneToOne(fetch = FetchType.LAZY,cascade=CascadeType.ALL)
#JoinColumn(name="type_id")
private Type type;
private Long viewCount; }
#Entity public class Images{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String imagePath;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "product_id")
private Product product; }
#Entity public class User implements UserDetails, Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotEmpty
private String fullName;
#NotEmpty
#Email
#Column(unique = true)
private String email;
#NotNull
#Column(unique = true)
private int phoneNumber;
#NotEmpty
#Size(min = 5)
private String password;
private Date createAt = new Date();
#Nullable
private String picPath;
#Nullable
private String token;
#ManyToMany
#JoinTable(name = "user_roles", joinColumns = {#JoinColumn(
name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "role_id")})
private List<Role> roles;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "user")
private Product product;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "user")
private View view; }
#Entity
public class Currency{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String code;
private String currency;
private String region_country;
#OneToOne(mappedBy = "currency", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Product product; }
#Entity
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String imagePath;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY,
mappedBy = "category")
private Product product;
#OneToMany(mappedBy = "category", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private Set<Brand> brands; }
#Entity public class Brand {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "category_id", nullable = false)
private Category category; }
#Entity public class View {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "user_id", nullable = false)
private User user;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "product_id", nullable = false)
private Product product; }
#Entity public class Type {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
private String name;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "type")
private Product product; }
#Id
private String role;
#ManyToMany(mappedBy = "roles")
private List<User> users;
}
More than one of your entities have each other in themselves.
For example, Product has an object of User, and User has an object of Product.
To solve this, you have to write
#JsonBackReference(value = "user-product")
private User user;
in the Product class,
and
#JsonManagedReference(value = "user-product")
private Product product;
In the user class.
Do it in every field and for every class that call each other.
Also, Check this out
JPA: Having lists on both ends without infinite loop
You have cycles in your data model. For example, Product holds Images and Images point back to Products.
This works in an object oriented world, because only pointer references are stored in those fields.
When serialized, however, the actual object is written out as json text. Your Product prints the Images object which in turn prints the Product object which again prints the Image object and so on.
You need to decide how you want to represent your json, map your database model into simple plain old java object and use this for serializations. These POJOs are often called View Model or Transport Objects.

springboot jpa combine two tables

I want to query data from two tables,
location field in Translation is a foreign key from id field of Location
#Entity
#Table(name = "Translation")
#Data
public class Translation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull private String language;
#NotNull private String name;
#NotNull private String description;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "location", nullable = false, insertable = false, updatable = false)
#Fetch(FetchMode.JOIN)
private Location location;
}
#Entity
#Table(name = "Location")
#Data
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull private String code;
#NotNull private String type;
private Double longitude;
private Double latitude;
#Column(name = "parent_id")
private Integer parentId;
#OneToMany(targetEntity = Translation.class, mappedBy="id", fetch = FetchType.EAGER)
private Set<Translation> translations;
}
————————————————————————————————————————
But when I use a query
#Query(
"SELECT new com.afkl.travel.exercise.model.RetrieveLocationResponse("
+ "loc.code, tran.name, loc.type, loc.latitude, loc.longitude, tran.description, loc.parentId)"
+ "FROM Location loc LEFT JOIN loc.translation tran")
List<RetrieveLocationResponse> fetchLeftJoin();
All the fields related to Translation is null, having no idea what happened
UPDATE
The following ones work for me.
#OneToMany(mappedBy = "location", cascade = CascadeType.ALL)
#JsonIgnore
private Set<Translation> translations;
#ManyToOne
#JoinColumn(name = "location")
private Location location;
try
#OneToMany(mappedBy = "location", cascade = CascadeType.ALL)
#JsonIgnore
private Set<Translation> translations;
#ManyToOne
#JoinColumn(name = "location")
private Location location;

one to many mapping in hibernate for a messaging service

I have two JPA entities: User and Message.
Each Message has one sender and one receiver of type User. And on the other side each User has two sets of type Message: inbox and outbox.
Message:
#Entity
#Table(name = "messages")
public class Message {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "seq")
#SequenceGenerator(name = "seq", sequenceName = "MESSAGES_SEQ")
#Column(name = "ID", insertable = false, updatable = false)
private int id;
#ManyToOne
#JoinColumn(name = "SENDER")
private User sender;
#ManyToOne
#JoinColumn(name = "RECEIVER")
private User receiver;
private String subject, content;
private Date sdate;
//getters and setters
}
All the properties which not being mapped with an annotation has he same name as the columns in database and are automatically mapped by JPA.
User:
#Entity
#Table(name = "users")
public class User {
#Column(name = "USERNAME")
private String username;
#Column(name = "PASSWORD")
private String pass;
#Column(name = "EMAIL")
private String email;
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "seq")
#SequenceGenerator(name = "seq", sequenceName = "USERS_SEQ")
#Column(name = "ID", insertable = false, updatable = false)
private int id;
#OneToMany(mappedBy = "uploader")
private Set<Book> books;
#OneToMany(mappedBy = "receiver")
private Set<Message> inbox;
#OneToMany(mappedBy = "sender")
private Set<Message> outbox;
//getters and setters
}
The problem is, when I select an User from Oracle database, then the inbox property is empty. How is this caused and how can I solve it?

Resources