mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-16 08:05:05 +00:00
HHH-12157 - TableGenerator defined on one class is not visible on another
(cherry picked from commit 8ae3dc4078597c69588ca8237591f1eab64c6bd7)
This commit is contained in:
parent
9fe921db52
commit
b1a9c816a7
@ -56,6 +56,7 @@
|
||||
import org.hibernate.cfg.CopyIdentifierComponentSecondPass;
|
||||
import org.hibernate.cfg.CreateKeySecondPass;
|
||||
import org.hibernate.cfg.FkSecondPass;
|
||||
import org.hibernate.cfg.IdGeneratorResolverSecondPass;
|
||||
import org.hibernate.cfg.JPAIndexHolder;
|
||||
import org.hibernate.cfg.PkDrivenByDefaultMapsIdSecondPass;
|
||||
import org.hibernate.cfg.PropertyData;
|
||||
@ -441,10 +442,12 @@ public void addIdentifierGenerator(IdentifierGeneratorDefinition generator) {
|
||||
if ( defaultIdentifierGeneratorNames.contains( generator.getName() ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
final IdentifierGeneratorDefinition old = idGeneratorDefinitionMap.put( generator.getName(), generator );
|
||||
if ( old != null ) {
|
||||
log.duplicateGeneratorName( old.getName() );
|
||||
if ( !old.equals( generator ) ) {
|
||||
throw new IllegalArgumentException( "Duplicate generator name " + old.getName() );
|
||||
}
|
||||
// log.duplicateGeneratorName( old.getName() );
|
||||
}
|
||||
}
|
||||
|
||||
@ -1442,6 +1445,7 @@ public Join locateJoin(Identifier tableName) {
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<IdGeneratorResolverSecondPass> idGeneratorResolverSecondPassList;
|
||||
private ArrayList<PkDrivenByDefaultMapsIdSecondPass> pkDrivenByDefaultMapsIdSecondPassList;
|
||||
private ArrayList<SetSimpleValueTypeSecondPass> setSimpleValueTypeSecondPassList;
|
||||
private ArrayList<CopyIdentifierComponentSecondPass> copyIdentifierComponentSecondPasList;
|
||||
@ -1460,7 +1464,10 @@ public void addSecondPass(SecondPass secondPass) {
|
||||
|
||||
@Override
|
||||
public void addSecondPass(SecondPass secondPass, boolean onTopOfTheQueue) {
|
||||
if ( secondPass instanceof PkDrivenByDefaultMapsIdSecondPass ) {
|
||||
if ( secondPass instanceof IdGeneratorResolverSecondPass ) {
|
||||
addIdGeneratorResolverSecondPass( (IdGeneratorResolverSecondPass) secondPass, onTopOfTheQueue );
|
||||
}
|
||||
else if ( secondPass instanceof PkDrivenByDefaultMapsIdSecondPass ) {
|
||||
addPkDrivenByDefaultMapsIdSecondPass( (PkDrivenByDefaultMapsIdSecondPass) secondPass, onTopOfTheQueue );
|
||||
}
|
||||
else if ( secondPass instanceof SetSimpleValueTypeSecondPass ) {
|
||||
@ -1518,6 +1525,13 @@ private void addSetSimpleValueTypeSecondPass(SetSimpleValueTypeSecondPass second
|
||||
addSecondPass( secondPass, setSimpleValueTypeSecondPassList, onTopOfTheQueue );
|
||||
}
|
||||
|
||||
private void addIdGeneratorResolverSecondPass(IdGeneratorResolverSecondPass secondPass, boolean onTopOfTheQueue) {
|
||||
if ( idGeneratorResolverSecondPassList == null ) {
|
||||
idGeneratorResolverSecondPassList = new ArrayList<>();
|
||||
}
|
||||
addSecondPass( secondPass, idGeneratorResolverSecondPassList, onTopOfTheQueue );
|
||||
}
|
||||
|
||||
private void addCopyIdentifierComponentSecondPass(
|
||||
CopyIdentifierComponentSecondPass secondPass,
|
||||
boolean onTopOfTheQueue) {
|
||||
@ -1573,8 +1587,8 @@ public void processSecondPasses(MetadataBuildingContext buildingContext) {
|
||||
inSecondPass = true;
|
||||
|
||||
try {
|
||||
processSecondPasses( idGeneratorResolverSecondPassList );
|
||||
processSecondPasses( implicitColumnNamingSecondPassList );
|
||||
|
||||
processSecondPasses( pkDrivenByDefaultMapsIdSecondPassList );
|
||||
processSecondPasses( setSimpleValueTypeSecondPassList );
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -763,15 +762,14 @@ else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
|
||||
}
|
||||
|
||||
// try to find class level generators
|
||||
HashMap<String, IdentifierGeneratorDefinition> classGenerators = buildLocalGenerators( clazzToProcess, context );
|
||||
|
||||
HashMap<String, IdentifierGeneratorDefinition> classGenerators = buildGenerators( clazzToProcess, context );
|
||||
// check properties
|
||||
final InheritanceState.ElementsToProcess elementsToProcess = inheritanceState.getElementsToProcess();
|
||||
inheritanceState.postProcess( persistentClass, entityBinder );
|
||||
|
||||
final boolean subclassAndSingleTableStrategy = inheritanceState.getType() == InheritanceType.SINGLE_TABLE
|
||||
&& inheritanceState.hasParents();
|
||||
Set<String> idPropertiesIfIdClass = new HashSet<String>();
|
||||
Set<String> idPropertiesIfIdClass = new HashSet<>();
|
||||
boolean isIdClass = mapAsIdClass(
|
||||
inheritanceStatePerClass,
|
||||
inheritanceState,
|
||||
@ -2134,7 +2132,7 @@ else if ( property.isAnnotationPresent( ManyToAny.class ) ) {
|
||||
}
|
||||
if ( property.isAnnotationPresent( CollectionId.class ) ) { //do not compute the generators unless necessary
|
||||
HashMap<String, IdentifierGeneratorDefinition> localGenerators = ( HashMap<String, IdentifierGeneratorDefinition> ) classGenerators.clone();
|
||||
localGenerators.putAll( buildLocalGenerators( property, context ) );
|
||||
localGenerators.putAll( buildGenerators( property, context ) );
|
||||
collectionBinder.setLocalGenerators( localGenerators );
|
||||
|
||||
}
|
||||
@ -2242,23 +2240,24 @@ else if ( !isId || !entityBinder.isIgnoreIdAnnotations() ) {
|
||||
final PropertyData mapsIdProperty = BinderHelper.getPropertyOverriddenByMapperOrMapsId(
|
||||
isId, propertyHolder, property.getName(), context
|
||||
);
|
||||
Map<String, IdentifierGeneratorDefinition> localGenerators = ( HashMap<String, IdentifierGeneratorDefinition> ) classGenerators.clone();
|
||||
final IdentifierGeneratorDefinition.Builder foreignGeneratorBuilder = new IdentifierGeneratorDefinition.Builder();
|
||||
foreignGeneratorBuilder.setName( "Hibernate-local--foreign generator" );
|
||||
foreignGeneratorBuilder.setStrategy( "foreign" );
|
||||
foreignGeneratorBuilder.addParam( "property", mapsIdProperty.getPropertyName() );
|
||||
|
||||
final IdentifierGeneratorDefinition foreignGenerator = foreignGeneratorBuilder.build();
|
||||
localGenerators.put( foreignGenerator.getName(), foreignGenerator );
|
||||
// Map<String, IdentifierGeneratorDefinition> localGenerators = ( HashMap<String, IdentifierGeneratorDefinition> ) classGenerators.clone();
|
||||
// localGenerators.put( foreignGenerator.getName(), foreignGenerator );
|
||||
|
||||
BinderHelper.makeIdGenerator(
|
||||
SecondPass secondPass = new IdGeneratorResolverSecondPass(
|
||||
( SimpleValue ) propertyBinder.getValue(),
|
||||
property,
|
||||
foreignGenerator.getStrategy(),
|
||||
foreignGenerator.getName(),
|
||||
context,
|
||||
localGenerators
|
||||
foreignGenerator
|
||||
);
|
||||
context.getMetadataCollector().addSecondPass( secondPass );
|
||||
}
|
||||
if ( isId ) {
|
||||
//components and regular basic types create SimpleValue objects
|
||||
@ -2348,8 +2347,9 @@ private static void processId(
|
||||
XClass entityXClass = inferredData.getClassOrElement();
|
||||
XProperty idXProperty = inferredData.getProperty();
|
||||
//clone classGenerator and override with local values
|
||||
HashMap<String, IdentifierGeneratorDefinition> localGenerators = ( HashMap<String, IdentifierGeneratorDefinition> ) classGenerators.clone();
|
||||
localGenerators.putAll( buildLocalGenerators( idXProperty, buildingContext ) );
|
||||
// HashMap<String, IdentifierGeneratorDefinition> localGenerators = ( HashMap<String, IdentifierGeneratorDefinition> ) classGenerators.clone();
|
||||
// localGenerators.putAll( buildGenerators( idXProperty, buildingContext ) );
|
||||
buildGenerators( idXProperty, buildingContext );
|
||||
|
||||
//manage composite related metadata
|
||||
//guess if its a component and find id data access (property, field etc)
|
||||
@ -2367,14 +2367,15 @@ private static void processId(
|
||||
//a component must not have any generator
|
||||
generatorType = "assigned";
|
||||
}
|
||||
BinderHelper.makeIdGenerator(
|
||||
|
||||
SecondPass secondPass = new IdGeneratorResolverSecondPass(
|
||||
idValue,
|
||||
idXProperty,
|
||||
generatorType,
|
||||
generatorName,
|
||||
buildingContext,
|
||||
localGenerators
|
||||
buildingContext
|
||||
);
|
||||
buildingContext.getMetadataCollector().addSecondPass( secondPass );
|
||||
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracev( "Bind {0} on {1}", ( isComponent ? "@EmbeddedId" : "@Id" ), inferredData.getPropertyName() );
|
||||
@ -2711,23 +2712,24 @@ public static Component fillComponent(
|
||||
if ( property.isAnnotationPresent( GeneratedValue.class ) &&
|
||||
property.isAnnotationPresent( Id.class ) ) {
|
||||
//clone classGenerator and override with local values
|
||||
Map<String, IdentifierGeneratorDefinition> localGenerators = new HashMap<String, IdentifierGeneratorDefinition>();
|
||||
localGenerators.putAll( buildLocalGenerators( property, buildingContext ) );
|
||||
// Map<String, IdentifierGeneratorDefinition> localGenerators = new HashMap<>();
|
||||
// localGenerators.putAll( buildGenerators( property, buildingContext ) );
|
||||
|
||||
buildGenerators( property, buildingContext );
|
||||
GeneratedValue generatedValue = property.getAnnotation( GeneratedValue.class );
|
||||
String generatorType = generatedValue != null
|
||||
? generatorType( generatedValue, buildingContext, property.getType() )
|
||||
: "assigned";
|
||||
String generator = generatedValue != null ? generatedValue.generator() : BinderHelper.ANNOTATION_STRING_DEFAULT;
|
||||
|
||||
BinderHelper.makeIdGenerator(
|
||||
SecondPass secondPass = new IdGeneratorResolverSecondPass(
|
||||
( SimpleValue ) comp.getProperty( property.getName() ).getValue(),
|
||||
property,
|
||||
generatorType,
|
||||
generator,
|
||||
buildingContext,
|
||||
localGenerators
|
||||
buildingContext
|
||||
);
|
||||
buildingContext.getMetadataCollector().addSecondPass( secondPass );
|
||||
}
|
||||
|
||||
}
|
||||
@ -2827,14 +2829,15 @@ private static void bindIdClass(
|
||||
id = value.make();
|
||||
}
|
||||
rootClass.setIdentifier( id );
|
||||
BinderHelper.makeIdGenerator(
|
||||
SecondPass secondPass = new IdGeneratorResolverSecondPass(
|
||||
id,
|
||||
inferredData.getProperty(),
|
||||
generatorType,
|
||||
generatorName,
|
||||
buildingContext,
|
||||
Collections.<String, IdentifierGeneratorDefinition>emptyMap()
|
||||
buildingContext
|
||||
);
|
||||
buildingContext.getMetadataCollector().addSecondPass( secondPass );
|
||||
|
||||
if ( isEmbedded ) {
|
||||
rootClass.setEmbeddedIdentifier( inferredData.getPropertyClass() == null );
|
||||
}
|
||||
@ -3329,7 +3332,7 @@ public static FetchMode getFetchMode(FetchType fetch) {
|
||||
}
|
||||
}
|
||||
|
||||
private static HashMap<String, IdentifierGeneratorDefinition> buildLocalGenerators(XAnnotatedElement annElt, MetadataBuildingContext context) {
|
||||
private static HashMap<String, IdentifierGeneratorDefinition> buildGenerators(XAnnotatedElement annElt, MetadataBuildingContext context) {
|
||||
HashMap<String, IdentifierGeneratorDefinition> generators = new HashMap<>();
|
||||
|
||||
TableGenerators tableGenerators = annElt.getAnnotation( TableGenerators.class );
|
||||
@ -3367,6 +3370,11 @@ private static HashMap<String, IdentifierGeneratorDefinition> buildLocalGenerato
|
||||
IdentifierGeneratorDefinition idGen = buildIdGenerator( genGen, context );
|
||||
generators.put( idGen.getName(), idGen );
|
||||
}
|
||||
|
||||
generators.forEach( (name, idGenerator) -> {
|
||||
context.getMetadataCollector().addIdentifierGenerator( idGenerator );
|
||||
} );
|
||||
|
||||
return generators;
|
||||
}
|
||||
|
||||
|
@ -42,12 +42,9 @@
|
||||
import org.hibernate.cfg.annotations.EntityBinder;
|
||||
import org.hibernate.cfg.annotations.Nullability;
|
||||
import org.hibernate.cfg.annotations.TableBinder;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.engine.config.spi.StandardConverters;
|
||||
import org.hibernate.id.IdentifierGenerator;
|
||||
import org.hibernate.id.MultipleHiLoPerTableGenerator;
|
||||
import org.hibernate.id.PersistentIdentifierGenerator;
|
||||
import org.hibernate.id.enhanced.SequenceStyleGenerator;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
@ -55,7 +52,6 @@
|
||||
import org.hibernate.mapping.Collection;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.IdGenerator;
|
||||
import org.hibernate.mapping.Join;
|
||||
import org.hibernate.mapping.MappedSuperclass;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
@ -82,7 +78,7 @@ private BinderHelper() {
|
||||
}
|
||||
|
||||
static {
|
||||
Set<String> primitiveNames = new HashSet<String>();
|
||||
Set<String> primitiveNames = new HashSet<>();
|
||||
primitiveNames.add( byte.class.getName() );
|
||||
primitiveNames.add( short.class.getName() );
|
||||
primitiveNames.add( int.class.getName() );
|
||||
@ -277,7 +273,7 @@ public static void createSyntheticPropertyReference(
|
||||
Object columnOwner = findColumnOwner( ownerEntity, columns[0].getReferencedColumn(), context );
|
||||
List<Property> properties = findPropertiesByColumns( columnOwner, columns, context );
|
||||
//create an embeddable component
|
||||
Property synthProp = null;
|
||||
Property synthProp;
|
||||
if ( properties != null ) {
|
||||
//todo how about properties.size() == 1, this should be much simpler
|
||||
Component embeddedComp = columnOwner instanceof PersistentClass ?
|
||||
@ -375,9 +371,9 @@ private static List<Property> findPropertiesByColumns(
|
||||
Object columnOwner,
|
||||
Ejb3JoinColumn[] columns,
|
||||
MetadataBuildingContext context) {
|
||||
Map<Column, Set<Property>> columnsToProperty = new HashMap<Column, Set<Property>>();
|
||||
List<Column> orderedColumns = new ArrayList<Column>( columns.length );
|
||||
Table referencedTable = null;
|
||||
Map<Column, Set<Property>> columnsToProperty = new HashMap<>();
|
||||
List<Column> orderedColumns = new ArrayList<>( columns.length );
|
||||
Table referencedTable;
|
||||
if ( columnOwner instanceof PersistentClass ) {
|
||||
referencedTable = ( (PersistentClass) columnOwner ).getTable();
|
||||
}
|
||||
@ -400,7 +396,7 @@ else if ( columnOwner instanceof Join ) {
|
||||
)
|
||||
);
|
||||
orderedColumns.add( column );
|
||||
columnsToProperty.put( column, new HashSet<Property>() );
|
||||
columnsToProperty.put( column, new HashSet<>() );
|
||||
}
|
||||
boolean isPersistentClass = columnOwner instanceof PersistentClass;
|
||||
Iterator it = isPersistentClass ?
|
||||
@ -416,7 +412,7 @@ else if ( columnOwner instanceof Join ) {
|
||||
//first naive implementation
|
||||
//only check 1 columns properties
|
||||
//TODO make it smarter by checking correctly ordered multi column properties
|
||||
List<Property> orderedProperties = new ArrayList<Property>();
|
||||
List<Property> orderedProperties = new ArrayList<>();
|
||||
for (Column column : orderedColumns) {
|
||||
boolean found = false;
|
||||
for (Property property : columnsToProperty.get( column ) ) {
|
||||
@ -714,7 +710,25 @@ public static void makeIdGenerator(
|
||||
id.setIdentifierGeneratorProperties( params );
|
||||
}
|
||||
|
||||
public static IdentifierGeneratorDefinition getIdentifierGenerator(
|
||||
/**
|
||||
* apply an id generator to a SimpleValue
|
||||
*/
|
||||
public static void makeIdGenerator(
|
||||
SimpleValue id,
|
||||
XProperty idXProperty,
|
||||
String generatorType,
|
||||
String generatorName,
|
||||
MetadataBuildingContext buildingContext,
|
||||
IdentifierGeneratorDefinition foreignKGeneratorDefinition) {
|
||||
Map<String, IdentifierGeneratorDefinition> localIdentifiers = null;
|
||||
if ( foreignKGeneratorDefinition != null ) {
|
||||
localIdentifiers = new HashMap<>();
|
||||
localIdentifiers.put( foreignKGeneratorDefinition.getName(), foreignKGeneratorDefinition );
|
||||
}
|
||||
makeIdGenerator( id, idXProperty, generatorType, generatorName, buildingContext, localIdentifiers );
|
||||
}
|
||||
|
||||
private static IdentifierGeneratorDefinition getIdentifierGenerator(
|
||||
String name,
|
||||
XProperty idXProperty,
|
||||
Map<String, IdentifierGeneratorDefinition> localGenerators,
|
||||
@ -1106,7 +1120,7 @@ static PropertyData getPropertyOverriddenByMapperOrMapsId(
|
||||
}
|
||||
|
||||
public static Map<String,String> toAliasTableMap(SqlFragmentAlias[] aliases){
|
||||
Map<String,String> ret = new HashMap<String,String>();
|
||||
Map<String,String> ret = new HashMap<>();
|
||||
for ( int i = 0; i < aliases.length; i++ ){
|
||||
if ( StringHelper.isNotEmpty( aliases[i].table() ) ){
|
||||
ret.put( aliases[i].alias(), aliases[i].table() );
|
||||
@ -1116,7 +1130,7 @@ public static Map<String,String> toAliasTableMap(SqlFragmentAlias[] aliases){
|
||||
}
|
||||
|
||||
public static Map<String,String> toAliasEntityMap(SqlFragmentAlias[] aliases){
|
||||
Map<String,String> ret = new HashMap<String,String>();
|
||||
Map<String,String> ret = new HashMap<>();
|
||||
for (int i = 0; i < aliases.length; i++){
|
||||
if (aliases[i].entity() != void.class){
|
||||
ret.put( aliases[i].alias(), aliases[i].entity().getName() );
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.cfg;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.mapping.SimpleValue;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class IdGeneratorResolverSecondPass implements SecondPass {
|
||||
private SimpleValue id;
|
||||
private XProperty idXProperty;
|
||||
private String generatorType;
|
||||
private String generatorName;
|
||||
private MetadataBuildingContext buildingContext;
|
||||
private IdentifierGeneratorDefinition localIdentifierGeneratorDefinition;
|
||||
|
||||
public IdGeneratorResolverSecondPass(
|
||||
SimpleValue id,
|
||||
XProperty idXProperty,
|
||||
String generatorType,
|
||||
String generatorName,
|
||||
MetadataBuildingContext buildingContext) {
|
||||
this.id = id;
|
||||
this.idXProperty = idXProperty;
|
||||
this.generatorType = generatorType;
|
||||
this.generatorName = generatorName;
|
||||
this.buildingContext = buildingContext;
|
||||
}
|
||||
|
||||
public IdGeneratorResolverSecondPass(
|
||||
SimpleValue id,
|
||||
XProperty idXProperty,
|
||||
String generatorType,
|
||||
String generatorName,
|
||||
MetadataBuildingContext buildingContext,
|
||||
IdentifierGeneratorDefinition localIdentifierGeneratorDefinition) {
|
||||
this(id,idXProperty,generatorType,generatorName,buildingContext);
|
||||
this.localIdentifierGeneratorDefinition = localIdentifierGeneratorDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doSecondPass(Map idGeneratorDefinitionMap) throws MappingException {
|
||||
BinderHelper.makeIdGenerator( id, idXProperty, generatorType, generatorName, buildingContext, localIdentifierGeneratorDefinition );
|
||||
}
|
||||
}
|
@ -18,8 +18,10 @@
|
||||
import org.hibernate.cfg.BinderHelper;
|
||||
import org.hibernate.cfg.Ejb3Column;
|
||||
import org.hibernate.cfg.Ejb3JoinColumn;
|
||||
import org.hibernate.cfg.IdGeneratorResolverSecondPass;
|
||||
import org.hibernate.cfg.PropertyData;
|
||||
import org.hibernate.cfg.PropertyInferredData;
|
||||
import org.hibernate.cfg.SecondPass;
|
||||
import org.hibernate.cfg.WrappedInferredData;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.mapping.Collection;
|
||||
@ -104,14 +106,15 @@ property, unique, associationTableBinder, ignoreNotFound, getBuildingContext()
|
||||
else {
|
||||
generatorType = null;
|
||||
}
|
||||
BinderHelper.makeIdGenerator(
|
||||
|
||||
SecondPass secondPass = new IdGeneratorResolverSecondPass(
|
||||
id,
|
||||
property,
|
||||
generatorType,
|
||||
generator,
|
||||
getBuildingContext(),
|
||||
localGenerators
|
||||
getBuildingContext()
|
||||
);
|
||||
buildingContext.getMetadataCollector().addSecondPass( secondPass );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user