| 
 | 1 | +/*  | 
 | 2 | + * SPDX-License-Identifier: LGPL-2.1-or-later  | 
 | 3 | + * Copyright Red Hat Inc. and Hibernate Authors  | 
 | 4 | + */  | 
 | 5 | +package org.hibernate.orm.test.tenantid;  | 
 | 6 | + | 
 | 7 | +import org.hibernate.annotations.TenantId;  | 
 | 8 | +import org.hibernate.boot.SessionFactoryBuilder;  | 
 | 9 | +import org.hibernate.boot.spi.MetadataImplementor;  | 
 | 10 | +import org.hibernate.cfg.AvailableSettings;  | 
 | 11 | +import org.hibernate.context.spi.CurrentTenantIdentifierResolver;  | 
 | 12 | +import org.hibernate.engine.spi.SessionFactoryImplementor;  | 
 | 13 | + | 
 | 14 | +import org.hibernate.testing.orm.junit.DomainModel;  | 
 | 15 | +import org.hibernate.testing.orm.junit.Jira;  | 
 | 16 | +import org.hibernate.testing.orm.junit.ServiceRegistry;  | 
 | 17 | +import org.hibernate.testing.orm.junit.SessionFactory;  | 
 | 18 | +import org.hibernate.testing.orm.junit.SessionFactoryProducer;  | 
 | 19 | +import org.hibernate.testing.orm.junit.SessionFactoryScope;  | 
 | 20 | +import org.hibernate.testing.orm.junit.Setting;  | 
 | 21 | +import org.junit.jupiter.api.BeforeAll;  | 
 | 22 | +import org.junit.jupiter.api.Test;  | 
 | 23 | + | 
 | 24 | +import jakarta.persistence.CascadeType;  | 
 | 25 | +import jakarta.persistence.Entity;  | 
 | 26 | +import jakarta.persistence.Id;  | 
 | 27 | +import jakarta.persistence.JoinColumn;  | 
 | 28 | +import jakarta.persistence.OneToOne;  | 
 | 29 | + | 
 | 30 | +import static org.assertj.core.api.Assertions.assertThat;  | 
 | 31 | + | 
 | 32 | +/**  | 
 | 33 | + * @author Marco Belladelli  | 
 | 34 | + */  | 
 | 35 | +@DomainModel( annotatedClasses = {  | 
 | 36 | +		TenantIdToOneBidirectionalTest.RootEntity.class,  | 
 | 37 | +		TenantIdToOneBidirectionalTest.ChildEntity.class,  | 
 | 38 | +} )  | 
 | 39 | +@SessionFactory  | 
 | 40 | +@ServiceRegistry( settings = @Setting( name = AvailableSettings.HBM2DDL_AUTO, value = "create-drop" ) )  | 
 | 41 | +@Jira( "https://hibernate.atlassian.net/browse/HHH-18617" )  | 
 | 42 | +public class TenantIdToOneBidirectionalTest implements SessionFactoryProducer {  | 
 | 43 | +	private static String currentTenant;  | 
 | 44 | + | 
 | 45 | +	@Test  | 
 | 46 | +	public void testExistingRoot(SessionFactoryScope scope) {  | 
 | 47 | +		currentTenant = "tenant_1";  | 
 | 48 | +		scope.inTransaction( session -> {  | 
 | 49 | +			final var child = session.find( ChildEntity.class, 1L );  | 
 | 50 | +			assertThat( child.getRoot() ).isNotNull().extracting( RootEntity::getChild ).isSameAs( child );  | 
 | 51 | +		} );  | 
 | 52 | +	}  | 
 | 53 | + | 
 | 54 | +	@Test  | 
 | 55 | +	public void testRemovedRoot(SessionFactoryScope scope) {  | 
 | 56 | +		currentTenant = "tenant_2";  | 
 | 57 | +		scope.inTransaction( session -> {  | 
 | 58 | +			final var child = session.find( ChildEntity.class, 2L );  | 
 | 59 | +			assertThat( child.getRoot() ).isNull();  | 
 | 60 | +		} );  | 
 | 61 | +	}  | 
 | 62 | + | 
 | 63 | +	@Test  | 
 | 64 | +	public void testNoRoot(SessionFactoryScope scope) {  | 
 | 65 | +		currentTenant = "tenant_3";  | 
 | 66 | +		scope.inTransaction( session -> {  | 
 | 67 | +			final var child = session.find( ChildEntity.class, 3L );  | 
 | 68 | +			assertThat( child.getRoot() ).isNull();  | 
 | 69 | +		} );  | 
 | 70 | +	}  | 
 | 71 | + | 
 | 72 | +	@BeforeAll  | 
 | 73 | +	public void setUp(SessionFactoryScope scope) {  | 
 | 74 | +		currentTenant = "tenant_1";  | 
 | 75 | +		scope.inTransaction( session -> {  | 
 | 76 | +			final var withChild = new RootEntity( 1L );  | 
 | 77 | +			withChild.setChild( new ChildEntity( 1L ) );  | 
 | 78 | +			session.persist( withChild );  | 
 | 79 | +		} );  | 
 | 80 | +		currentTenant = "tenant_2";  | 
 | 81 | +		scope.inTransaction( session -> {  | 
 | 82 | +			final var deletedRoot = new RootEntity( 2L );  | 
 | 83 | +			final var child = new ChildEntity( 2L );  | 
 | 84 | +			deletedRoot.setChild( child );  | 
 | 85 | +			session.persist( deletedRoot );  | 
 | 86 | + | 
 | 87 | +			session.flush();  | 
 | 88 | +			session.clear();  | 
 | 89 | +			session.remove( deletedRoot );  | 
 | 90 | +		} );  | 
 | 91 | +		currentTenant = "tenant_3";  | 
 | 92 | +		scope.inTransaction( session -> session.persist( new ChildEntity( 3L ) ) );  | 
 | 93 | +	}  | 
 | 94 | + | 
 | 95 | +	@Override  | 
 | 96 | +	public SessionFactoryImplementor produceSessionFactory(MetadataImplementor model) {  | 
 | 97 | +		final SessionFactoryBuilder sfb = model.getSessionFactoryBuilder();  | 
 | 98 | +		sfb.applyCurrentTenantIdentifierResolver( new CurrentTenantIdentifierResolver<String>() {  | 
 | 99 | +			@Override  | 
 | 100 | +			public String resolveCurrentTenantIdentifier() {  | 
 | 101 | +				return currentTenant;  | 
 | 102 | +			}  | 
 | 103 | + | 
 | 104 | +			@Override  | 
 | 105 | +			public boolean validateExistingCurrentSessions() {  | 
 | 106 | +				return false;  | 
 | 107 | +			}  | 
 | 108 | +		} );  | 
 | 109 | +		return (SessionFactoryImplementor) sfb.build();  | 
 | 110 | +	}  | 
 | 111 | + | 
 | 112 | +	@Entity( name = "RootEntity" )  | 
 | 113 | +	static class RootEntity {  | 
 | 114 | +		@Id  | 
 | 115 | +		private Long id;  | 
 | 116 | + | 
 | 117 | +		@TenantId  | 
 | 118 | +		private String tenant;  | 
 | 119 | + | 
 | 120 | +		@OneToOne( cascade = CascadeType.PERSIST )  | 
 | 121 | +		@JoinColumn  | 
 | 122 | +		private ChildEntity child;  | 
 | 123 | + | 
 | 124 | +		public RootEntity() {  | 
 | 125 | +		}  | 
 | 126 | + | 
 | 127 | +		public RootEntity(Long id) {  | 
 | 128 | +			this.id = id;  | 
 | 129 | +		}  | 
 | 130 | + | 
 | 131 | +		public ChildEntity getChild() {  | 
 | 132 | +			return child;  | 
 | 133 | +		}  | 
 | 134 | + | 
 | 135 | +		public void setChild(ChildEntity child) {  | 
 | 136 | +			this.child = child;  | 
 | 137 | +		}  | 
 | 138 | +	}  | 
 | 139 | + | 
 | 140 | +	@Entity( name = "ChildEntity" )  | 
 | 141 | +	static class ChildEntity {  | 
 | 142 | +		@Id  | 
 | 143 | +		private Long id;  | 
 | 144 | + | 
 | 145 | +		@TenantId  | 
 | 146 | +		private String tenant;  | 
 | 147 | + | 
 | 148 | +		@OneToOne( mappedBy = "child" )  | 
 | 149 | +		private RootEntity root;  | 
 | 150 | + | 
 | 151 | +		public ChildEntity() {  | 
 | 152 | +		}  | 
 | 153 | + | 
 | 154 | +		public ChildEntity(Long id) {  | 
 | 155 | +			this.id = id;  | 
 | 156 | +		}  | 
 | 157 | + | 
 | 158 | +		public RootEntity getRoot() {  | 
 | 159 | +			return root;  | 
 | 160 | +		}  | 
 | 161 | +	}  | 
 | 162 | +}  | 
0 commit comments