Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions ci/jpa-3.1-tck.Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ pipeline {
stages {
stage('Build') {
steps {
script {
docker.withRegistry('https://index.docker.io/v1/', 'hibernateci.hub.docker.com') {
docker.image('eclipse-temurin:11').pull()
}
}
dir('hibernate') {
checkout scm
sh './gradlew publishToMavenLocal -PmavenMirror=nexus-load-balancer-c4cf05fd92f43ef8.elb.us-east-1.amazonaws.com -DjakartaJpaVersion=3.1.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,12 @@ public PersistentCollection<?> load(Object key, SharedSessionContractImplementor
MULTI_KEY_LOAD_LOGGER.debugf( "Batch loading entity `%s#%s`", getLoadable().getNavigableRole().getFullPath(), key );
}
final ForeignKeyDescriptor keyDescriptor = getLoadable().getKeyDescriptor();
if ( keyDescriptor.isEmbedded() ) {
if ( keyDescriptor.isEmbedded()
|| keyDescriptor.getKeyPart().getSingleJdbcMapping().getValueConverter() != null ) {
assert keyDescriptor.getJdbcTypeCount() == 1;
return loadEmbeddable( key, session, keyDescriptor );
return loadWithConversion( key, session, keyDescriptor );
}
else {

final Object[] keysToInitialize = resolveKeysToInitialize( key, session );
initializeKeys( keysToInitialize, session );

Expand All @@ -134,7 +134,7 @@ public PersistentCollection<?> load(Object key, SharedSessionContractImplementor
}
}

private PersistentCollection<?> loadEmbeddable(
private PersistentCollection<?> loadWithConversion(
Object keyBeingLoaded,
SharedSessionContractImplementor session,
ForeignKeyDescriptor keyDescriptor) {
Expand All @@ -148,14 +148,14 @@ private PersistentCollection<?> loadEmbeddable(
.getComponentType(),
length
);
final Object[] embeddedKeys = (Object[]) Array.newInstance( keyDomainType, length );
final Object[] domainKeys = (Object[]) Array.newInstance( keyDomainType, length );
session.getPersistenceContextInternal().getBatchFetchQueue()
.collectBatchLoadableCollectionKeys(
length,
(index, key) ->
keyDescriptor.forEachJdbcValue( key, (i, value, jdbcMapping) -> {
jdbcKeysToInitialize[index] = value;
embeddedKeys[index] = key;
domainKeys[index] = key;
}, session )
,
keyBeingLoaded,
Expand All @@ -164,7 +164,7 @@ private PersistentCollection<?> loadEmbeddable(

initializeKeys( jdbcKeysToInitialize, session );

for ( Object initializedKey : embeddedKeys ) {
for ( Object initializedKey : domainKeys ) {
finishInitializingKey( initializedKey, session );
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.batch;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import org.assertj.core.api.Assertions;
import org.hibernate.HibernateException;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Type;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.testing.jdbc.SQLStatementInspector;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.StringJavaType;
import org.hibernate.type.internal.UserTypeJavaTypeWrapper;
import org.hibernate.usertype.EnhancedUserType;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import static org.hibernate.type.SqlTypes.VARCHAR;

@ServiceRegistry(
settings = {
@Setting( name = AvailableSettings.DIALECT_NATIVE_PARAM_MARKERS, value = "false" )
}
)
@DomainModel(
annotatedClasses = {
BatchAndUserTypeIdCollectionTest.Child.class,
BatchAndUserTypeIdCollectionTest.Parent.class
}
)
@SessionFactory(
useCollectingStatementInspector = true
)

public class BatchAndUserTypeIdCollectionTest {

@BeforeAll
public void setUp(SessionFactoryScope scope) {

Check notice

Code scanning / CodeQL

Unread local variable Note test

Variable 'Child child1' is never read.
scope.inTransaction(

Check notice

Code scanning / CodeQL

Unread local variable Note test

Variable 'Child child2' is never read.
session -> {

Check notice

Code scanning / CodeQL

Unread local variable Note test

Variable 'Child child3' is never read.
for (long i = 1L; i < 11; i++) {

Check notice

Code scanning / CodeQL

Unread local variable Note test

Variable 'Child child4' is never read.
Parent parent = new Parent( new Parent.ParentId( "parent-" + i ) );

Check notice

Code scanning / CodeQL

Unread local variable Note test

Variable 'Child child5' is never read.
Child child1 = new Child( i * 100L + 1L, parent );

Check notice

Code scanning / CodeQL

Unread local variable Note test

Variable 'Child child6' is never read.
Child child2 = new Child( i * 100L + 2L, parent );

Check notice

Code scanning / CodeQL

Unread local variable Note test

Variable 'Child child7' is never read.
Child child3 = new Child( i * 100L + 3L, parent );

Check notice

Code scanning / CodeQL

Unread local variable Note test

Variable 'Child child8' is never read.
Child child4 = new Child( i * 100L + 4L, parent );

Check notice

Code scanning / CodeQL

Unread local variable Note test

Variable 'Child child9' is never read.
Child child5 = new Child( i * 100L + 5L, parent );

Check notice

Code scanning / CodeQL

Unread local variable Note test

Variable 'Child child10' is never read.
Child child6 = new Child( i * 100L + 6L, parent );

Check notice

Code scanning / CodeQL

Unread local variable Note test

Variable 'Child child11' is never read.
Child child7 = new Child( i * 100L + 7L, parent );
Child child8 = new Child( i * 100L + 8L, parent );
Child child9 = new Child( i * 100L + 9L, parent );
Child child10 = new Child( i * 100L + 10L, parent );
Child child11 = new Child( i * 100L + 11L, parent );
session.persist( parent );
}
}
);
}

@Test
public void testBatchInitializeChildCollection(SessionFactoryScope scope){
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
scope.inTransaction(
session -> {
statementInspector.clear();
final List<Parent> list = session.createSelectionQuery( "from Parent", Parent.class )
.getResultList();
list.get( 0 ).getChildren().size();
statementInspector.assertExecutedCount( 2 );
Assertions.assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( "?" );
if ( scope.getSessionFactory().getJdbcServices().getDialect().useArrayForMultiValuedParameters() ) {
Assertions.assertThat( statementInspector.getSqlQueries().get( 1 ) ).containsOnlyOnce( "?" );
}
else {
Assertions.assertThat( statementInspector.getSqlQueries().get( 1 ) ).containsOnlyOnce( "in (?,?,?,?,?)" );
}
}
);
}

@Entity(name = "Child")
@Table(name = "child_table")
public static class Child {
@Id
private Long id;

private String name;

@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;

public Child() {
}

public Child(Long id, Parent parent) {
this.id = id;
this.name = String.valueOf( id );
this.parent = parent;
parent.addChild( this );
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

public Parent getParent() {
return parent;
}

}

@Entity(name = "Parent")
@Table(name = "parents")
public static class Parent {
@Id
@Type(ParentIdUserType.class)
private ParentId id;

private String name;

@BatchSize(size = 5)
@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
public Set<Child> children = new HashSet<>();

public Parent() {
}

public Parent(ParentId id) {
this.id = id;
this.name = String.valueOf( id );
}

public ParentId getId() {
return id;
}

public String getName() {
return name;
}

public Set<Child> getChildren() {
return children;
}

public void addChild(Child child){
children.add( child );
}

public static class ParentId implements Serializable {

private static final long serialVersionUID = 1L;

private final String id;

@Override
public String toString() {
return id;
}

public ParentId(String id) {
this.id = id;
}

public String getId() {
return id;
}

@Override
public boolean equals(Object o) {
if ( o == null || getClass() != o.getClass() ) {
return false;
}
ParentId parentId = (ParentId) o;
return Objects.equals( id, parentId.id );
}

@Override
public int hashCode() {
return Objects.hashCode( id );
}
}


static class ParentIdUserType implements EnhancedUserType<ParentId> {
@Override
public int getSqlType() {
return VARCHAR;
}

@Override
public Class<ParentId> returnedClass() {
return ParentId.class;
}

@Override
public boolean equals(ParentId x, ParentId y) {
return Objects.equals(x, y);
}

@Override
public int hashCode(ParentId x) {
return x.hashCode();
}

@Override
public ParentId nullSafeGet(ResultSet rs, int position,
SharedSessionContractImplementor session, Object owner)
throws SQLException {
String string = rs.getString( position );
return rs.wasNull() ? null : new ParentId(string);
}

@Override
public void nullSafeSet(PreparedStatement st, ParentId value, int index,
SharedSessionContractImplementor session)
throws SQLException {
if ( value == null ) {
st.setNull(index, VARCHAR);
}
else {
st.setString(index, value.getId());
}
}

@Override
public boolean isMutable() {
return false;
}

@Override
public ParentId deepCopy(ParentId parentId) {
return parentId; //ParentId is immutable
}

@Override
public Serializable disassemble(ParentId parentId) {
return parentId; //ParentId is immutable
}

@Override
public ParentId assemble(Serializable cached, Object owner) {
return (ParentId) cached; //ParentId is immutable
}

@Override
public String toSqlLiteral(ParentId parentId) {
return parentId.getId();
}

@Override
public String toString(ParentId parentId) throws HibernateException {
return parentId.getId();
}

@Override
public ParentId fromStringValue(CharSequence sequence) throws HibernateException {
return new ParentId(sequence.toString());
}

@Override
public BasicValueConverter<ParentId, Object> getValueConverter() {
return (BasicValueConverter) new BasicValueConverter<ParentId, String>() {
@Override
public String toRelationalValue(ParentId value) {
return value == null ? null : value.getId();
}

@Override
public ParentId toDomainValue(String dbData) {
return dbData == null ? null : new ParentId(dbData);
}

@Override
public JavaType<ParentId> getDomainJavaType() {
return new UserTypeJavaTypeWrapper<>(ParentIdUserType.this);
}

@Override
public JavaType<String> getRelationalJavaType() {
return StringJavaType.INSTANCE;
}
};
}
}
}
}
Loading