Hibernate Unable to find logical name - java

I have encountered this exception
Caused by:
org.hibernate.MappingException: Unable to find column with logical name: picture_id in org.hibernate.mapping.Table(TXN_INVENTORY_PICTURE) and its related supertables and secondary tables
at org.hibernate.cfg.Ejb3JoinColumn.checkReferencedColumnsType(Ejb3JoinColumn.java:564)
at org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:258)
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:116)
at org.hibernate.cfg.Configuration.processEndOfQueue(Configuration.java:1596)
at org.hibernate.cfg.Configuration.processFkSecondPassInOrder(Configuration.java:1519)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1420)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1844)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
Here's a gist of the classes that are involve
#Entity
#Table(name = "REF_PRODUCT")
public class Product extends JpaModel{
#Column(name= "NAME")
private String name;
#Column(name = "MANUFACTURER")
private String manufacturer;
/**
* Work around for missing
*/
#OneToOne(optional = true)
#JoinColumn(name="picture",referencedColumnName = "picture_id")
private InventoryItemPicture picture;
}
And here's The inventoryItemPicutre
#Entity
#Table(name = "TXN_INVENTORY_PICTURE")
public class InventoryItemPicture extends JpaModel{
#Column
private byte[] image;
#Column
private String fileName;
public InventoryItemPicture(){
}
....
}
And here's the JPAModel
#MappedSuperclass
public abstract class JpaModel {
#Id
#Column(name ="ID", columnDefinition = "serial")
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
#Column(name = "created_date")
private DateTime createdDate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public DateTime getCreatedDate() {
return createdDate;
}
public void setCreatedDate(DateTime createdDate) {
this.createdDate = createdDate;
}
}
NOTE: Omitted the setters and getters,and what I am trying to achieve here is The picture can be null in the Product class

You have an error in your mapping, referencedColumnName targets the column picture_id in TXN_INVENTORY_PICTURE table, which doesn't exist.
If picture_id is the name of the column in REF_PRODUCT table which is a foreign key to TXN_INVENTORY_PICTURE table, then change the mapping to this
#OneToOne(optional = true)
#JoinColumn(name="picture_id")
private InventoryItemPicture picture;

Related

get API in spring boot with one to many relation

I have two models that are having one to many relation (customers have many invoices)
so i create one - many relation on it, this is my customer class :
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name = "serial_number")
private long serialNumber;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#Column(name = "mobile_number")
private String mobileNumber;
#Column(name = "is_deleted")
private boolean isDeleted;
#OneToMany
private Set <Invoice> invoices;
}
and this is invoices class :
#Entity
#Table(name = "invoice")
public class Invoice {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name = "serial_number")
private long serialNumber;
#Column(name = "status")
private String status;
#Column(name = "created_date")
private Timestamp createdDate;
#Column(name = "is_deleted")
private boolean isDeleted;
#ManyToOne
#JoinColumn(name = "customer_id")
private Customer customer;
}
and then i create GET API ( get customers ) but it's nor working and return this error :
nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not extract ResultSet (through reference chain: java.util.ArrayList[0]->com.example.invoices.model.Customer["invoices"]), path=/customer/viewList}]
and this is my api :
public List<Customer> getAllCustomers() {
List<Customer> customers = cutomerRepository.findAll();
return customers;
}
and controller :
#GetMapping("/viewList")
public ResponseEntity<List<Customer>> getAllCustomers() {
List<Customer> customers = new ArrayList<>();
customers = customerService.getAllCustomers();
if (customers.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(customers, HttpStatus.OK);
}
You have a Bidirectional relation and therefore an endless loop if json tries to deserialize the Object.
You can use #JsonIgnore to break the loop or use DTOs to return at the endpoint
#Entity
#Table(name = "invoice")
public class Invoice {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name = "serial_number")
private long serialNumber;
#Column(name = "status")
private String status;
#Column(name = "created_date")
private Timestamp createdDate;
#Column(name = "is_deleted")
private boolean isDeleted;
#ManyToOne
#JsonIgnore
#JoinColumn(name = "customer_id")
private Customer customer;
}
DTO would look something like this (I like to use records for this but since I don't know if you use Java 17 I still use class):
Customer:
#Data
public class CustomerDTO {
private final int id;
private final long serialNumber;
private final String firstName;
private final String lastName;
private final String email;
private final String mobileNumber;
private final boolean isDeleted;
private final Set <Invoice> invoices;
public static CustomerDTO fromModel(Customer customer) {
return new CustomerDTO(
customer.getId(),
customer.getSerialNumber(),
customer.getFirstName(),
customer.getLastName(),
customer.getEmail(),
customer.getMobileNumber(),
customer.isDeleted(),
customer.getInvoices()
.stream()
.map(InvoiceDTO::fromModel)
.collect(Collectors.toSet())
);
}
}
Invoice (here you don't show the customer again):
#Data
public class InvoiceDTO {
private final int id;
private final String status;
private final Timestamp createdDate;
private final boolean isDeleted;
public static InvoiceDTO fromModel(Invoice invoice) {
return new InvoiceDTO(
invoice.getId(),
invoice.getStatus(),
invoice.getCreatedDate(),
invoice.isDeleted()
);
}
}
Controller:
#GetMapping("/viewList")
public ResponseEntity<List<CustomerDTO>> getAllCustomers() {
List<CustomerDTO> customers = new ArrayList<>();
customers = customerService.getAllCustomers()
.stream()
.map(CustomerDTO::fromModel)
.toList() //Depending on Java Version .collect(Collectors.toList());
if (customers.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(customers., HttpStatus.OK);
}
Do not open the entity class directly to the outside world
As DTO use for example:
public class InvoiceDTO {
private int id;
private long serialNumber;
private String status;
private Timestamp createdDate;
private boolean isDeleted;
private CustomerDTO customer;
}
See it applied in my GitHub repo FurnitureStoreApplication, example DTO classes in package dto:

Are Nested/Child JPA Entities required to have a value when performing UPDATE operation

I have a Section entity. Section entity has a nested GradeLevel entity annotated with #OneToOne
Section
#Entity
#Setter
#Getter
public class Section{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true, nullable = false)
private String name;
#OneToOne
private GradeLevel gradeLevel;
#Column(columnDefinition = "boolean default true")
private Boolean isActive = true;
#CreationTimestamp
#Column(name = "date_created")
private LocalDateTime dateCreated = LocalDateTime.now();
#UpdateTimestamp
#Column(name = "date_last_updated")
private LocalDateTime dateLastUpdated = LocalDateTime.now();
}
GradeLevel
#Entity
#Getter
#Setter
public class GradeLevel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true, nullable = false)
private String code; //GL-01, GL-02...
#Column(unique = true)
private String description; //Kinder 1, Kinder 2, Grade 1....
private String category; //Elementary School, Junior Highschool, Senior Highschool
#Column(columnDefinition = "boolean default true")
private Boolean isActive = true;
#CreationTimestamp
#Column(name = "date_created")
private LocalDateTime dateCreated = LocalDateTime.now();
#UpdateTimestamp
#Column(name = "date_last_updated")
private LocalDateTime dateLastUpdated = LocalDateTime.now();
}
The #OneToOne GradeLevel gradeLevel; creates a foreign key in the Section table that references to GradeLevel's ID column.
The table looks like this:
Now if I want to update just the name of the Section and pass just the Section ID and new Section Name without the GradeLevel ID request sample below,
{
"id" : 1,
"name" : "Venus" }
The compiler complains about a null GradeLevel ID
org.hibernate.HibernateException: identifier of an instance of com.jordan.cdoautoformsettingsservice.entities.GradeLevel was altered from 1 to null...
This gives me the impression that when we have nested JPA entities with #OneToOne (or #OneToMany...), we are required to provide the ID of the nested Entity. In this case, it wants the ID of the GradeLevel
I fails to complete the UPDATE operation.
ServiceImpl.java (UPDATE method) below
#Override
public Section updateSection(Section request) throws Exception {
logger.debug("request : "+request);
Long sectionId = request.getId();
Optional<Section> optionalSection = sectionRepo.findById(sectionId); //retrieve the section we want to update
if(!optionalSection.isPresent()){
throw new Exception("Section with ID: "+request.getId()+ "is NOT FOUND");
}
GradeLevel gradeLevel = optionalSection.get().getGradeLevel();
gradeLevel.setId(request.getGradeLevel().getId());
logger.debug("GradeLevel Properties: ");
logger.debug("GradeLevel ID: "+gradeLevel.getId());
logger.debug("GradeLevel CODE: "+gradeLevel.getCode());
logger.debug("GradeLevel DESCRIPTION: "+gradeLevel.getDescription());
logger.debug("GradeLevel CATEGORY: "+gradeLevel.getCategory());
Section section = optionalSection.get();
section.setName(request.getName()); //replace current section name with section name FROM REQUEST
section.setDateLastUpdated(request.getDateLastUpdated());
section.setIsActive(request.getIsActive());
section.setGradeLevel(gradeLevel);
return sectionRepo.save(section);
}
I'd appreciate any suggestion or thoughts.
Thank you.
this is error due to auto generated entity will generate new section id at every time if you add a constructor in Section.java that has argument id value. then save function work fine
Section.java
package com.example.updatehibernate.entity;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.*;
import java.time.LocalDateTime;
#Entity
public class Section{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true, nullable = false)
private String name;
#OneToOne
private GradeLevel gradeLevel;
#Column(columnDefinition = "boolean default true")
private Boolean isActive = true;
#CreationTimestamp
#Column(name = "date_created")
private LocalDateTime dateCreated = LocalDateTime.now();
#UpdateTimestamp
#Column(name = "date_last_updated")
private LocalDateTime dateLastUpdated = LocalDateTime.now();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public GradeLevel getGradeLevel() {
return gradeLevel;
}
public void setGradeLevel(GradeLevel gradeLevel) {
this.gradeLevel = gradeLevel;
}
public Boolean getActive() {
return isActive;
}
public void setActive(Boolean active) {
isActive = active;
}
public LocalDateTime getDateCreated() {
return dateCreated;
}
public void setDateCreated(LocalDateTime dateCreated) {
this.dateCreated = dateCreated;
}
public LocalDateTime getDateLastUpdated() {
return dateLastUpdated;
}
public void setDateLastUpdated(LocalDateTime dateLastUpdated) {
this.dateLastUpdated = dateLastUpdated;
}
public Section(Long id, String name, GradeLevel gradeLevel, Boolean isActive, LocalDateTime dateCreated, LocalDateTime dateLastUpdated) {
this.id = id;
this.name = name;
this.gradeLevel = gradeLevel;
this.isActive = isActive;
this.dateCreated = dateCreated;
this.dateLastUpdated = dateLastUpdated;
}
public Section(Long id, String name) {
this.id = id;
this.name = name;
}
public Section() {
}
}

One-To-Many Relationship in Spring Data JPA

I would like to have a One-to-many relationship between 2 Entities, Consumer and Policy. One consumer should have several policies.
This is an example of a Consumer JSON object I would like to have:
{
id : 1,
name : "Peter",
endpoint: "123.456.778",
policies: [
{
id : 1,
name: "policy 01"
},
{
id : 2,
name: "policy 02"
}
]
}
This is what I have so far:
Policy Entity
#Entity
public class Policy {
#Id
#GeneratedValue
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
//getters and setters
}
Consumer Entity
#Entity
public class Consumer {
#Id
#GeneratedValue
#Column(name = "consumer_id")
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "endpoint")
private String endpoint;
#OneToMany
#JoinColumn(??)
private List<Policy> policies;
//getters and setters
}
It's not that hard I think, but im trying now for several hours and can't get it done. I'm new to Spring, so if someone is able to help me, I would be very thankfull!
#Entity
public class Consumer {
#OneToMany(mappedBy = "consumer")
private List<Policy> policies;
}
#Entity
public class Policy {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn("consumer_id")
private Consumer consumer;
}
fetch = FetchType.LAZY is not necessary, but desirable.
I have provided some basics here
what is #JoinColumn and how it is used in Hibernate
If you want to a Policy don't have a Consumer:
You can use a join table
#Entity
public class Consumer {
#OneToMany
private List<Policy> policies;
}
#Entity
public class Policy {
}
A unidirectional relation (a Policy table will have consumer_id column, but a Policy class doesn't have a Consumer)
#Entity
public class Consumer {
#OneToMany
#JoinColumn("consumer_id")
private List<Policy> policies;
}
#Entity
public class Policy {
}
Also, keep in mind, that if you want to use a Policy as tabular data (from a dictionary) you will need #ManyToMany.
Try this code :)
Your Consumer Class
#Entity
public class Consumer {
#Id
#GeneratedValue
#Column(name = "consumer_id")
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "endpoint")
private String endpoint;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idPolicy")
private List<Policy> policies;
public Consumer() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
public List<Policy> getPolicies() {
return policies;
}
public void setPolicies(List<Policy> policies) {
this.policies = policies;
}
}
Be aware that in the mapped by, you should use the name of the column that references Policy in your database, so if it's no policyId, use the name you gave to it
Your Policy Class
#Entity
public class Policy {
#Id
#GeneratedValue
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#ManyToOne(optional = false)
private Consumer consumer;
public Policy() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

QueryMethodParameterConversionException

I'm trying to access JPA Data with REST using spring-boot-starter-data-rest.
I want to use a different method than the CrudRepository has. But the framework responds with the following exception:
exception is org.springframework.data.repository.support.QueryMethodParameterConversionException: Failed to convert Brazil into hello.Country!] with root cause
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [#org.springframework.data.repository.query.Param hello.Country]
Endpoint
http://localhost:8080/rest/cities/search/findByCountry?name=Brazil
CityRepository.java
#RepositoryRestResource(collectionResourceRel = "cities", path = "cities")
public interface CityRepository extends CrudRepository<City, Long> {
List<City> findByCountry(#Param("name") Country country);
}
City.java
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
#Entity
#Table(name = "City")
public class City implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#JsonInclude(value=Include.ALWAYS)
private long id;
#NotNull
#Column(name="name")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "country_id")
#Embedded
private Country country;
protected City() {}
public City(long id, String nome) {
this.id = id;
this.name = nome;
}
public Country getCountry() {
return country;
}
Country.java
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
#Entity
public class Country implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#NotNull
#Column(name = "name")
private String name;
#OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;
protected Country() {}
public Country(long id, String nome) {
this.id = id;
this.name = nome;
}
When I call http://localhost:8080/rest/cities/ , I get the list of cities normally. I setted the configureRepositoryRestConfiguration to config.setBasePath("/rest");
java.lang.String != hello.Country
In accordance with the documentation, we need to:
Use ResponseBody with consumes
Or create object from String like in REST hello world example
I did the following to resolve this issue, using a nativeQuery:
#Query(value = "SELECT * FROM City c JOIN Country co
ON c.country_id = co.id WHERE co.name
LIKE (CONCAT_WS('%',:name, '%'))", nativeQuery = true)
List<City> country(#Param("name") String name);

Wrong query generated with #OrderBy annotation

I am using Hibernate3 and i have an entity with the following collection:
#ManyToMany
#JoinTable(name = "buys_publishers", joinColumns=#JoinColumn(name="buy_id", referencedColumnName = "buy_id"), inverseJoinColumns=#JoinColumn(name = "publisher_id", referencedColumnName = "publisher_id"))
#OrderBy("name")
private List<Publisher> publishers;
(fetch and cascade decelerations omitted)
The target entity (Publisher) inherits from an entity that holds the "name" attribute on which the #orderby is activated.
here is the target entity:
#Entity
#Table(name="publishers")
#PrimaryKeyJoinColumn(name="account_id")
public class Publisher extends Account{
/**
*
*/
private static final long serialVersionUID = 1L;
#Column(name = "publisher_id")
private Long publisherId;
public Long getPublisherId() {
return publisherId;
}
public void setPublisherId(Long publisherId) {
this.publisherId = publisherId;
}
}
and the super class:
#Entity
#Table(name="accounts")
#Inheritance(strategy=InheritanceType.JOINED)
public abstract class Account implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#Column(name="id",unique=true, nullable=false )
#GeneratedValue( strategy = IDENTITY )
private long id;
#Column(name = "name")
private String name;
#Column(name = "account_type")
#Enumerated(EnumType.ORDINAL)
private AccountType accountType;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public AccountType getAccountType() {
return accountType;
}
public void setAccountType(AccountType accountType) {
this.accountType = accountType;
}
}
the query that Hibernate generates is:
select publishers0_.buy_id as buy1_1_, publishers0_.publisher_id as publisher2_1_, publisher1_.account_id as id6_0_, publisher1_1_.account_type as account2_6_0_, publisher1_1_.name as name6_0_, publisher1_.publisher_id as publisher1_18_0_ from buys_publishers publishers0_ left outer join publishers publisher1_ on publishers0_.publisher_id=publisher1_.publisher_id left outer join accounts publisher1_1_ on publisher1_.account_id=publisher1_1_.id where publishers0_.buy_id=? order by accounts.name asc
it is clear from the query that the order by should be on publisher1_1_, am i doing something wrong or is this a bug?
Thank you.
This looks like HHH-4260 - #OrderBy does not work with inherited properties (and I wouldn't expect a short term resolution).

Categories

Resources