Skip to content

Commit

Permalink
HBX-2053 Class naming in hibernate.reveng.xml
Browse files Browse the repository at this point in the history
  -Adjust table/classname matching logic in OverrideRepository.
  -New test for entity generation with custom entity package and classname.
  -Test for h2 lowercase identifier generation.
  -Adjust the db test utility to allow for an alternate hibernate.properties.
  • Loading branch information
dareni authored and koentsje committed Feb 2, 2024
1 parent 9c421c8 commit fac2702
Show file tree
Hide file tree
Showing 15 changed files with 411 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public class OverrideRepository {

final private Set<TableColumnKey> excludedColumns;

final private Map<TableIdentifier, String> tableToClassName;
final private TableToClassName tableToClassName;

final private List<SchemaSelection> schemaSelections;

Expand Down Expand Up @@ -99,7 +99,7 @@ public OverrideRepository() {
identifierPropertiesForTable = new HashMap<TableIdentifier, Properties>();
primaryKeyColumnsForTable = new HashMap<TableIdentifier, List<String>>();
propertyNameForPrimaryKey = new HashMap<TableIdentifier, String>();
tableToClassName = new HashMap<TableIdentifier, String>();
tableToClassName = new TableToClassName();
excludedColumns = new HashSet<TableColumnKey>();
schemaSelections = new ArrayList<SchemaSelection>();
compositeIdNameForTable = new HashMap<TableIdentifier, String>();
Expand Down Expand Up @@ -600,12 +600,23 @@ public void addTable(Table table, String wantedClassName) {
existing.add( fk );
}

tables.add(table);

if(StringHelper.isNotEmpty(wantedClassName)) {
tableToClassName.put(TableIdentifier.create(table), wantedClassName);
TableIdentifier tableIdentifier = TableIdentifier.create(table);
String className = wantedClassName;
/* If wantedClassName specifies a package, it is given by
<hibernate-reverse-engineering><table class="xxx"> config so do no more. */
if(!wantedClassName.contains(".")) {
/* Now look for the package name specified by
<hibernate-reverse-engineering><table-filter package="xxx"> config. */
String packageName = getPackageName(tableIdentifier);
if (packageName != null && !packageName.isBlank()) {
className = packageName + "." + wantedClassName;
}
}
tableToClassName.put(tableIdentifier, className);
}
}
tables.add(table);
}

static class TableColumnKey {
private TableIdentifier query;
Expand Down Expand Up @@ -739,7 +750,60 @@ public void addMetaAttributeInfo(

}




/*It is not possible to match a table on TableMapper alone because RootClassBinder.bind()
calls nullifyDefaultCatalogAndSchema(table) before doing this TableToClassName lookup.
So only use the table name for initial matching, and catalog or schema names when they
are not null.
*/

private class TableToClassName {
Map<String, TableMapper> map = new HashMap<String, TableMapper>();

private String get(TableIdentifier tableIdentifier) {
TableMapper mapper = map.get(tableIdentifier.getName());
if (mapper != null) {
if (mapper.catalog == null || tableIdentifier.getCatalog() == null ||
mapper.catalog.equals(tableIdentifier.getCatalog())){
if (mapper.schema == null || tableIdentifier.getSchema() == null ||
mapper.schema.equals(tableIdentifier.getSchema())){
if (mapper.packageName.length() == 0) {
return mapper.className;
} else {
return mapper.packageName + "." + mapper.className;
}
}
}
}
return null;
}

private void put(TableIdentifier tableIdentifier, String wantedClassName) {
TableMapper tableMapper = new TableMapper(
tableIdentifier.getCatalog(),
tableIdentifier.getSchema(),
tableIdentifier.getName(),
wantedClassName);
map.put(tableIdentifier.getName(), tableMapper);
}
}

private class TableMapper {
String catalog;
String schema;
String className;
String packageName;

private TableMapper(String catalog, String schema, String name, String wantedClassName) {
this.catalog = catalog;
this.schema = schema;
if (wantedClassName.contains(".")) {
int nameStartPos = wantedClassName.lastIndexOf(".");
this.className = wantedClassName.substring(nameStartPos+1);
this.packageName = wantedClassName.substring(0, nameStartPos);
} else {
this.className = wantedClassName;
this.packageName = "";
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Hibernate Tools, Tooling for your Hibernate Projects
*
* Copyright 2004-2021 Red Hat, Inc.
*
* Licensed under the GNU Lesser General Public License (LGPL),
* version 2.1 or later (the "License").
* You may not use this file except in compliance with the License.
* You may read the licence in the 'lgpl.txt' file in the root folder of
* project or obtain a copy at
*
* http://www.gnu.org/licenses/lgpl-2.1.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hibernate.tool.entitynaming;

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Properties;

import org.hibernate.tool.api.export.Exporter;
import org.hibernate.tool.api.export.ExporterConstants;
import org.hibernate.tool.api.export.ExporterFactory;
import org.hibernate.tool.api.export.ExporterType;
import org.hibernate.tool.api.metadata.MetadataDescriptor;
import org.hibernate.tool.api.metadata.MetadataDescriptorFactory;
import org.hibernate.tool.api.reveng.RevengSettings;
import org.hibernate.tool.api.reveng.RevengStrategy;
import org.hibernate.tool.api.reveng.RevengStrategyFactory;
import org.hibernate.tools.test.util.JdbcUtil;
import org.hibernate.tools.test.util.ResourceUtil;
import org.jboss.logging.Logger;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.io.TempDir;

/**
* Test lowercase database identifiers and specified entity package and class name.
*
* Settings: 1. Adjust hibernate.properties to clear hibernate.default_schema and
* hibernate.default_catalog. Setting these values fail the classname/tablename matching logic
* because of a call to nullifyDefaultCatalogAndSchema(table) in RootClassBinder.bind() before the
* TableToClassName logic.
*
* 2. Create a custom RevengStrategy (eg RevengStrategyEntityNaming.java) with the required
* SchemaSelection settings.
*
* @author Daren
*/
@TestInstance(Lifecycle.PER_CLASS)
public class EntityNamingTest {

private static final Logger log = Logger.getLogger(EntityNamingTest.class);

@TempDir
public File outputDir = new File("output");

static final String packageName = "com.entity";

Properties hibernateProperties = new Properties();

@BeforeAll
public void setUp() {
JdbcUtil.createDatabase(this);
try {
hibernateProperties.load(JdbcUtil.getAlternateHibernateProperties(this));
if (log.isInfoEnabled()) {
log.info(hibernateProperties.toString());
}
} catch (IOException ex) {
throw new RuntimeException("Alternate hibernate.properties does not exist!", ex);
}
}

@AfterAll
public void tearDown() {
JdbcUtil.dropDatabase(this);
}

@Test
public void testGenerateJava() throws IOException {
File[] revengFiles = new File[]{
ResourceUtil.resolveResourceFile(this.getClass(), "reveng.xml")};

RevengStrategy reveng = RevengStrategyFactory.createReverseEngineeringStrategy(
null, revengFiles);

reveng = new RevengStrategyEntityNaming(reveng);

//Necessary to set the root strategy.
RevengSettings revengSettings
= new RevengSettings(reveng).setDefaultPackageName(packageName)
.setDetectManyToMany(true)
.setDetectOneToOne(true)
.setDetectOptimisticLock(true);

reveng.setSettings(revengSettings);
MetadataDescriptor metadataDescriptor = MetadataDescriptorFactory
.createReverseEngineeringDescriptor(reveng, hibernateProperties);

Exporter exporter = ExporterFactory.createExporter(ExporterType.JAVA);
exporter.getProperties().put(ExporterConstants.METADATA_DESCRIPTOR, metadataDescriptor);
exporter.getProperties().put(ExporterConstants.DESTINATION_FOLDER, outputDir);
exporter.getProperties().setProperty("ejb3", "true");
exporter.start();
String packageDir = outputDir + File.separator
+ packageName.replace(".", File.separator);
File dummy = new File(packageDir, "Dummy.java");
assertTrue(dummy.exists());
File order = new File(packageDir, "Order.java");
assertTrue(order.exists());
File orderItem = new File(packageDir, "OrderItem.java");
assertTrue(orderItem.exists());
String str = new String(Files.readAllBytes(orderItem.toPath()));
assertTrue(str.contains("private Integer oiId;"));
assertTrue(str.contains("private Order order;"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2023 Hibernate.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.hibernate.tool.entitynaming;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.mapping.Column;
import org.hibernate.tool.api.reveng.RevengStrategy;
import org.hibernate.tool.api.reveng.TableIdentifier;
import org.hibernate.tool.internal.reveng.strategy.DelegatingStrategy;
import org.hibernate.tool.internal.util.NameConverter;

/**
*
* @author Daren
*/
public class RevengStrategyEntityNaming extends DelegatingStrategy {

private List<SchemaSelection> schemas;

public RevengStrategyEntityNaming(RevengStrategy delegate) {
super(delegate);
this.schemas = new ArrayList<>();
schemas.add(new SchemaSelection(){
@Override
public String getMatchCatalog() {
/* no h2 pattern matching on catalog*/
return "test1";
}

@Override
public String getMatchSchema() {
return "PUBLIC";
}

@Override
public String getMatchTable() {
return ".*";
}
});
}

public List<SchemaSelection> getSchemaSelections() {
return schemas;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CREATE TABLE "orders" ( "ordId" INT generated by default as identity (start with 1) NOT NULL, "ordDesc" VARCHAR(10), "ordVersion" TINYINT DEFAULT 0 NOT NULL, PRIMARY KEY ("ordId"))
CREATE TABLE "orderItems" ( "oiId" INT generated by default as identity (start with 1) NOT NULL, "oiOrdId" INT NOT NULL, "oiDesc" VARCHAR(10), "oiVersion" TINYINT DEFAULT 0 NOT NULL, PRIMARY KEY ("oiId"))
ALTER TABLE "orderItems" ADD CONSTRAINT "orderorderitemfk" FOREIGN KEY ("oiOrdId") REFERENCES "orders" ("ordId") ON DELETE CASCADE ON UPDATE CASCADE
CREATE TABLE "dummy" ( "duId" INT generated by default as identity (start with 1) NOT NULL, "duVersion" TINYINT DEFAULT 0 NOT NULL, PRIMARY KEY ("duId"))
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
DROP TABLE "orderItems"
DROP TABLE "orders"
DROP TABLE "dummy"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
hibernate.dialect org.hibernate.dialect.H2Dialect
hibernate.connection.driver_class org.h2.Driver
hibernate.connection.username sa
hibernate.connection.password
hibernate.connection.url jdbc:h2:mem:test1;DATABASE_TO_UPPER=FALSE
hibernate.default_schema
hibernate.default_catalog
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-reverse-engineering SYSTEM "http://hibernate.org/dtd/hibernate-reverse-engineering-3.0.dtd" >

<hibernate-reverse-engineering>
<table-filter match-catalog="test1" match-schema="PUBLIC" match-name=".*" package="com.entity"/>
<table name="orderItems" class="OrderItem"/>
<table name="orders" class="Order"/>
</hibernate-reverse-engineering>
Loading

0 comments on commit fac2702

Please sign in to comment.