hibernate 6 fetching to much at once - sql-server

I have some big entities with lots of columns and selfreferences. Somthing like this:
#Entity public class Person{
private String firstName;
private String lastName;
private String title;
.... 50 more columns ....
#ManyToOne
#JoinColumns({
#JoinColumn(name = "k1", rerencedColumnName = "k1"),
#JoinColumn(name = "k2", referencedColumnName = "k2") })
private Person representation;
}
#Entity public class Assignment{
#ManyToOne #JoinColumn(...)
private Person leader;
#ManyToOne #JoinColumn(...)
private Person employee;
}
That usually worked. But ever since upgrading to Hibernate6 all these columns and manyToOnes are joinfetched which frequently results in:
Caused by: org.hibernate.exception.SQLGrammarException: Error advancing (next) ResultSet position
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:89) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
SQLStateConversionDelegate.java:89
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:56) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
StandardSQLExceptionConverter.java:56
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
SqlExceptionHelper.java:109
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
SqlExceptionHelper.java:95
... 201 common frames omitted
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Eine Zeile der Größe 9020 kann nicht erstellt werden, da sie länger als die zulässige maximale Zeilengröße von 8060 wäre.
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:262) ~[mssql-jdbc-7.4.1.jre8.jar:na]
SQLServerException.java:262
at com.microsoft.sqlserver.jdbc.SQLServerResultSet$FetchBuffer.nextRow(SQLServerResultSet.java:5415) ~[mssql-jdbc-7.4.1.jre8.jar:na]
SQLServerResultSet.java:5415
at com.microsoft.sqlserver.jdbc.SQLServerResultSet.fetchBufferNext(SQLServerResultSet.java:1758) ~[mssql-jdbc-7.4.1.jre8.jar:na]
SQLServerResultSet.java:1758
at com.microsoft.sqlserver.jdbc.SQLServerResultSet.next(SQLServerResultSet.java:1016) ~[mssql-jdbc-7.4.1.jre8.jar:na]
SQLServerResultSet.java:1016
at com.zaxxer.hikari.pool.HikariProxyResultSet.next(HikariProxyResultSet.java) ~[HikariCP-5.0.1.jar:na]
at org.hibernate.sql.results.jdbc.internal.JdbcValuesResultSetImpl.lambda$processNext$0(JdbcValuesResultSetImpl.java:89) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
I don't quite get why the exceptionmessage is in german but it says a row of the size 9020 can't be created since it is bigger than the maximum allowed size of 8000

Related

Why Hibernate doesnt create a join query

I have ValidationErrorAsset table which have 7 foreign key columns referenced to ValidationErrorMaster.
I wrote Entity classes for them assuming Hibernate will fetch data using some join (less concerned inner/outer for now).
But Hibernate actually writes 8 simple select queries. 1 for ValidationErrorAsset and other 7 for ValidationErrorMaster, gets data and creates the object.
This involves total 8 network calls which is so costly.
Please help me fixing this.
Below are 2 Entities and Hibernate generated queries:
ValidationErrorAsset .class
#Entity
public class ValidationErrorAsset {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer validationErrorAssetId;
#ManyToOne
#JoinColumn(name = "asset_type_id")
private ValidationErrorMaster assetTypeError;
#ManyToOne
#JoinColumn(name = "asset_group_id")
private ValidationErrorMaster assetGroupError;
#ManyToOne
#JoinColumn(name = "name")
private ValidationErrorMaster nameError;
#ManyToOne
#JoinColumn(name = "asset_family_type_id")
private ValidationErrorMaster assetFamilyTypeError;
#ManyToOne
#JoinColumn(name = "source_repository_id")
private ValidationErrorMaster sourceRepositoryError;
#ManyToOne
#JoinColumn(name = "source_repository_path")
private ValidationErrorMaster sourceRepositoryPathError;
#ManyToOne
#JoinColumn(name = "asset_publisher_type_id")
private ValidationErrorMaster assetPublisherTypeError;
}
ValidationErrorMaster.class
#Entity
public class ValidationErrorMaster {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer validationErrorMasterId;
private String errorCode;
private String description;
private Boolean activeInd;
private String updtUsername;
private Integer updtCnt=0;
private LocalDateTime updtDtTm=LocalDateTime.now(ZoneOffset.UTC);;
private String severityLevel;
private Boolean skippable;
}
Hibernate generated MS-SQL query:
Hibernate:
select
validation0_.validation_error_asset_id as validati1_35_,
validation0_.active_ind as active_i2_35_,
validation0_.error_obj as error_ob3_35_,
validation0_.updt_cnt as updt_cnt4_35_,
validation0_.updt_dt_tm as updt_dt_5_35_,
validation0_.updt_username as updt_use6_35_,
validation0_.asset_family_type_id as asset_fa7_35_,
validation0_.asset_group_id as asset_gr8_35_,
validation0_.asset_id as asset_id9_35_,
validation0_.asset_publisher_type_id as asset_p10_35_,
validation0_.asset_type_id as asset_t11_35_,
validation0_.name as name12_35_,
validation0_.source_repository_id as source_13_35_,
validation0_.source_repository_path as source_14_35_
from
validation_error_asset validation0_
Hibernate:
select
validation0_.validation_error_master_id as validati1_36_0_,
validation0_.active_ind as active_i2_36_0_,
validation0_.description as descript3_36_0_,
validation0_.error_code as error_co4_36_0_,
validation0_.severity_level as severity5_36_0_,
validation0_.skippable as skippabl6_36_0_,
validation0_.updt_cnt as updt_cnt7_36_0_,
validation0_.updt_dt_tm as updt_dt_8_36_0_,
validation0_.updt_username as updt_use9_36_0_
from
validation_error_master validation0_
where
validation0_.validation_error_master_id=?
Hibernate:
select
validation0_.validation_error_master_id as validati1_36_0_,
validation0_.active_ind as active_i2_36_0_,
validation0_.description as descript3_36_0_,
validation0_.error_code as error_co4_36_0_,
validation0_.severity_level as severity5_36_0_,
validation0_.skippable as skippabl6_36_0_,
validation0_.updt_cnt as updt_cnt7_36_0_,
validation0_.updt_dt_tm as updt_dt_8_36_0_,
validation0_.updt_username as updt_use9_36_0_
from
validation_error_master validation0_
where
validation0_.validation_error_master_id=?
Rest 4 I skipped to add.

How to update tables with many-to-many relationship when performing crud operations in Spring Boot

I'm trying to create a Spring Boot backend for my project. In the database I have Deck and Word tables with a many-to-many relationship connected via DeckWord table. The bridge table has additional fields and a composite PK consisting of the other 2 tables' PK's.
I am not sure about how I should structure the crud operations in my project. Say I'm trying to add a new word and it should be assigned to a certain deck. What model's controller should handle the post operation in that scenario: Word or DeckWord? Should the Deck's List<DeckWord> be updated as well?
UPDATE:
Included the models, omitted the getters, setters and constructors
#Entity
#Table(name = "deck")
public class Deck {
#Id
#SequenceGenerator(
name = "deck_sequence",
sequenceName = "deck_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "deck_sequence"
)
#Column(name = "deck_id")
private Long id;
#Transient
private Boolean learnt;
private String name;
#OneToMany(mappedBy = "deck", cascade = CascadeType.ALL)
private List<DeckWord> deckwords;
#ManyToOne
#JoinColumn(name="appuser_id",referencedColumnName="appuser_id")
private Appuser appuser;
}
and
#Entity
#Table(name = "word")
public class Word {
#Id
#SequenceGenerator(
name = "word_sequence",
sequenceName = "word_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "word_sequence"
)
#Column(name = "word_id")
private Long id;
private String definition;
private String transcription;
#OneToMany(mappedBy = "word", cascade = CascadeType.ALL)
private List<DeckWord> deckwords;
}
and the bridge table:
#Embeddable
class DeckWordKey implements Serializable {
#Column(name = "deck_id")
Long deckId;
#Column(name = "word_id")
Long wordId;
}
#Entity
#Table
public class DeckWord {
#EmbeddedId
DeckWordKey id;
#ManyToOne
#MapsId("deckId")
#JoinColumn(name = "deck_id",referencedColumnName="deck_id")
Deck deck;
#ManyToOne
#MapsId("wordId")
#JoinColumn(name = "word_id",referencedColumnName="word_id")
Word word;
private Boolean learnt;
private LocalDate last_checked;
private WordGroup wordGroup;
}
Answering your questions:
What model's controller should handle the post operation in that scenario: Word or DeckWord?
Given that a Word should always be assigned to a Deck, then I would use a POST request to the URL "/decks/{deckId}/words" to create a new Word. The request body should include definition and transcription.
Should the Deck's List be updated as well?
Yes, it must. For that, you need to use deckId that you receive as a path parameter.

Spring JPA inserts duplicate select columns when using #MapsId, #AttributeOverride on embedded composite key

I'm developing a Spring Boot REST API application and I've encountered a problem with SQL generation for Transact-SQL (SQL Server) dialect and I'm not sure where I did something wrong.
The application is about storage management and I have two entities: Part and Stock. I've simplified the structure to be as simple as possible.
I have composite PK - PartPK:
#Data #Embeddable
class PartPK {
#Column(name = "PART_ID")
private String partId;
#Column(name = "PART_ORGANIZATION_ID")
private String orgId;
}
... and the entity Part having PartPK as #EmbeddedId:
#Entity #Table(name = "parts")
class Part {
#EmbeddedId
private PartPK id;
}
then I'm having a Stock entity that ties to a Part entity and stores. The entity has a composite PK with the following structure, where I'm overriding attributes from PartPK (giving them STOCK_ prefix)
#Data #Embeddable
class StockPK {
#Column(name = "STOCK_STORE_ID")
private String storeId;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "partId", column = #Column(name = "STOCK_PART_ID")),
#AttributeOverride(name = "orgId", column = #Column(name = "STOCK_PART_ORGANIZATION_ID")),
})
private PartPK partId;
}
... and enclosing Stock entity where I'm trying to reference the Part entity using #MapsId:
#Entity #Table(name = "stocks")
class Stock {
#EmbeddedId
private StockPK id;
#MapsId("partId")
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "STOCK_PART_ID", referencedColumnName = "PART_ID"),
#JoinColumn(name = "STOCK_PART_ORGANIZATION_ID", referencedColumnName = "PART_ORGANIZATION_ID"),
})
private Part part;
}
Which compiles, but after executing a query from the repository, it generates the following query:
select TOP (?)
stockdb0_.stock_part_organization_id as bis_part0_0_,
stockdb0_.stock_store_id as bis_stor3_0_,
stockdb0_.stock_part_organization_id as bis_part5_0_,
stockdb0_.stock_part_id as bis_part6_0_
from stocks stockdb0_
As you can notice, for some reason it uses 2 times stock_part_organization_id column. The entity has incorrect values after persistence mapping (two Stock rows having the same Store but different parts are considered to be the same entity). When the part attribute is removed from the Stock entity, the query and resulting persistence mapping is correct.
Is there anything I'm doing wrong?
I'm using Spring Boot 2.4.5 (the latest) and Started Data Jpa of the same version.
I think using #IdClass will work better in this case:
class StockPK implements Serializable {
private String storeId;
private Part part;
...
}
#Entity #Table(name = "stocks")
#IdClass(StockPK.class)
class Stock {
#Id
private String id;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "STOCK_PART_ID", referencedColumnName = "PART_ID"),
#JoinColumn(name = "STOCK_PART_ORGANIZATION_ID", referencedColumnName = "PART_ORGANIZATION_ID"),
})
private Part part;
...
}
But if you want to use #EmbeddedId:
#Embeddable
public static class StockPK implements Serializable {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "STOCK_PART_ID", referencedColumnName = "PART_ID"),
#JoinColumn(name = "STOCK_PART_ORGANIZATION_ID", referencedColumnName = "PART_ORGANIZATION_ID"),
})
private Part part;
#Column(name = "STOCK_STORE_ID")
private String storeId;
}
#Entity #Table(name = "stocks")
class Stock {
#EmbeddedId
private StockPK id;
// The association is already defined in the key
}
Anyway, you don't have to use #MapsId (that's for something else) and you can find examples of both approaches with more details in the Hibernate ORM documentation.

How to get comma separated value using JPA in Database

enter image description here
Currently I've been problem what get my database data.. Because That's fileIdx Column was be Comma Seperated value
Problem is..
Caused by: java.sql.SQLException: Out of range value for column 'fileidx7_4_' : value 322,323
at org.mariadb.jdbc.internal.com.read.resultset.rowprotocol.TextRowProtocol.getInternalLong(TextRowProtocol.java:349)
at org.mariadb.jdbc.internal.com.read.resultset.SelectResultSet.getLong(SelectResultSet.java:1001)
at org.mariadb.jdbc.internal.com.read.resultset.SelectResultSet.getLong(SelectResultSet.java:995)
at com.zaxxer.hikari.pool.HikariProxyResultSet.getLong(HikariProxyResultSet.java)
at org.hibernate.type.descriptor.sql.BigIntTypeDescriptor$2.doExtract(BigIntTypeDescriptor.java:63)
at org.hibernate.type.descriptor.sql.BasicExtractor.extract(BasicExtractor.java:47)
at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:257)
at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:253)
at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:243)
at org.hibernate.type.AbstractStandardBasicType.hydrate(AbstractStandardBasicType.java:329)
at org.hibernate.type.ManyToOneType.hydrate(ManyToOneType.java:184)
at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:3088)
at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1907)
at org.hibernate.loader.Loader.hydrateEntityState(Loader.java:1835)
at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1808)
at org.hibernate.loader.Loader.getRow(Loader.java:1660)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:745)
at org.hibernate.loader.Loader.getRowsFromResultSet(Loader.java:1044)
at org.hibernate.loader.Loader.processResultSet(Loader.java:995)
at org.hibernate.loader.Loader.doQuery(Loader.java:964)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:350)
at org.hibernate.loader.Loader.doList(Loader.java:2887)
... 108 more
It is my Entity ↓↓↓↓↓
#Entity
public class Board {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idx;
private Long code;
private Long adminIdx;
private String write;
private String title;
private String content;
private String fileIdx;
private String deleted;
#Column(name = "sDate")
private Long sdate;
#Column(name = "eDate")
private Long edate;
private Long regdate;
It is my JpaRepository Method used JPQL ↓↓↓↓↓
#Query(value = "select b from Board b")
List<Board> getTest();
It is my TestCode ↓↓↓↓↓
#Test
public void test(){
List<Board> shopBoardEntity = boardRepository.getTest();
shopBoardEntity.forEach(o -> {
System.out.println("dddd : " + o.getTitle());
});
}
No matter how hard I try, I don't know..
Anyone Idea??

#ManyToOne mapping without an entity for the intermediate table

I am writing an application that will query a massive Database, that cannot be changed.
For that reason, my application does not need to map all Objects, since that would be useless and time consuming.
All entities there mapped are #Immutable.
I came across this relationship:
I want to Map Order, and have it reference Customer. It is, in fact, a Many to One Relationship, it just happens two be two Join clauses away.
I am not interested in neither R nor B, since they convey no information related to my requirement.
I envision something like this, but I know the syntax is invalid:
#Entity
#Immutable
#Table(name = "Order")
public class Order implements Serializable {
#Id
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumns(value =
#JoinColumn(table = "R", name = "id", referencedColumnName = "R_id"),
#JoinColumn(table = "Customer", name = "id", referencedColumnName = "Customer_id")
)
private Customer customer;
... more data and getters/setters omitted ...
}
#Entity
#Immutable
#Table(name = "Customer")
public class Customer implements Serializable {
#Id
#Column(name = "id")
private Long id;
... more data and getters/setters omitted ...
}
Is there a way I can do this, without creating an entity for R?
EDIT: -------------------------
I tried the following, as per suggestion:
#ManyToOne
#JoinTable(name = "R",
joinColumns = #JoinColumn(name = "id", referencedColumnName = "R_id"),
inverseJoinColumns = #JoinColumn(name = "id", referencedColumnName = "Customer_id"))
private Customer customer;
However, I get the following error:
Unable to find column with logical name: R_id in org.hibernate.mapping.Table(Order) and its related supertables and secondary tables
You could use the #JoinTable annotation for the following schema.
in this way
#Entity
#Table(name = "Order")
public class Order {
// ...
#ManyToOne
#JoinTable(
name = "R",
joinColumns = #JoinColumn(name = "ord_id"),
inverseJoinColumns = #JoinColumn(name = "customer_id"))
private Customer customer;
// ...
}
But for your case it looks like not possible to avoid usage of entity for intermediate table R due to the lack of foreign key to the Order table in the R.

Resources