Skip to content

Commit c229f3a

Browse files
committed
add documentation
1 parent 832c298 commit c229f3a

File tree

1 file changed

+145
-2
lines changed

1 file changed

+145
-2
lines changed

docs/src/main/asciidoc/hibernate-orm.adoc

Lines changed: 145 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,9 +560,9 @@ See <<multitenancy>>.
560560
`io.quarkus.hibernate.orm.runtime.tenant.TenantConnectionResolver`::
561561
See <<programmatically-resolving-tenants-connections>>.
562562
`org.hibernate.boot.model.FunctionContributor`::
563-
Contribute custom SQL functions.
563+
See <<custom-functions-and-types>>.
564564
`org.hibernate.boot.model.TypeContributor`::
565-
Contribute custom types.
565+
See <<custom-functions-and-types>>.
566566

567567
[[persistence-unit-active]]
568568
=== Activate/deactivate persistence units
@@ -1578,6 +1578,149 @@ public class ExampleTenantConnectionResolver implements TenantConnectionResolver
15781578
}
15791579
----
15801580

1581+
[[custom-functions-and-types]]
1582+
=== Custom functions, types and mappings
1583+
1584+
To register custom SQL functions or types, in Hibernate ORM, you can implement the standard Hibernate interfaces:
1585+
1586+
* `org.hibernate.boot.model.FunctionContributor`
1587+
* `org.hibernate.boot.model.TypeContributor`
1588+
1589+
Creating an application-scoped bean that implements one of these interfaces and annotating it with `@PersistenceUnitExtension`
1590+
(or `@PersistenceUnitExtension("nameOfYourPU")` for a <<multiple-persistence-units,named persistence unit>>)
1591+
will automatically register it with the corresponding persistence unit.
1592+
1593+
Here is an example of a custom function contributor:
1594+
1595+
[source,java]
1596+
----
1597+
import java.util.List;
1598+
import org.hibernate.boot.model.FunctionContributions;
1599+
import org.hibernate.boot.model.FunctionContributor;
1600+
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
1601+
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
1602+
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
1603+
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
1604+
import org.hibernate.sql.ast.SqlAstTranslator;
1605+
import org.hibernate.sql.ast.spi.SqlAppender;
1606+
import org.hibernate.sql.ast.tree.SqlAstNode;
1607+
import org.hibernate.sql.ast.tree.expression.ReturnableType;
1608+
import org.hibernate.sql.ast.tree.expression.SqlAstNodeRenderingMode;
1609+
import org.hibernate.type.StandardBasicTypes;
1610+
import org.hibernate.type.spi.TypeConfiguration;
1611+
import io.quarkus.hibernate.orm.PersistenceUnitExtension;
1612+
import jakarta.enterprise.context.ApplicationScoped;
1613+
1614+
@ApplicationScoped
1615+
@PersistenceUnitExtension
1616+
public class CustomFunctionContributor implements FunctionContributor {
1617+
1618+
@Override
1619+
public void contributeFunctions(FunctionContributions functionContributions) {
1620+
functionContributions.getFunctionRegistry().register(
1621+
"addHardcodedSuffix",
1622+
new HardcodedSuffixFunction(
1623+
functionContributions.getTypeConfiguration(), "_some_suffix"));
1624+
}
1625+
1626+
private static final class HardcodedSuffixFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
1627+
private final String suffix;
1628+
1629+
private HardcodedSuffixFunction(TypeConfiguration typeConfiguration, String suffix) {
1630+
super("addHardcodedSuffix",
1631+
StandardArgumentsValidators.exactly(1),
1632+
StandardFunctionReturnTypeResolvers.invariant(
1633+
typeConfiguration.getBasicTypeRegistry().resolve(StandardBasicTypes.STRING)),
1634+
StandardFunctionArgumentTypeResolvers.impliedOrInvariant(typeConfiguration, StandardBasicTypes.STRING)
1635+
);
1636+
this.suffix = suffix;
1637+
}
1638+
1639+
@Override
1640+
public void render(SqlAppender sqlAppender, List<? extends SqlAstNode> sqlAstArguments, ReturnableType<?> returnType,
1641+
SqlAstTranslator<?> walker) {
1642+
sqlAppender.appendSql('(');
1643+
walker.render(sqlAstArguments.get(0), SqlAstNodeRenderingMode.DEFAULT);
1644+
sqlAppender.appendSql(" || '" + suffix + "')");
1645+
}
1646+
}
1647+
}
1648+
1649+
----
1650+
1651+
And here is an example of a custom type contributor that registers a custom `UserType` (e.g. mapping a Boolean to "Y"/"N"):
1652+
1653+
[source,java]
1654+
----
1655+
import java.sql.PreparedStatement;
1656+
import java.sql.ResultSet;
1657+
import java.sql.SQLException;
1658+
import org.hibernate.boot.model.TypeContributions;
1659+
import org.hibernate.boot.model.TypeContributor;
1660+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1661+
import org.hibernate.service.ServiceRegistry;
1662+
import org.hibernate.type.SqlTypes;
1663+
import org.hibernate.type.descriptor.WrapperOptions;
1664+
import org.hibernate.usertype.UserType;
1665+
import io.quarkus.hibernate.orm.PersistenceUnitExtension;
1666+
import jakarta.enterprise.context.ApplicationScoped;
1667+
1668+
@ApplicationScoped
1669+
@PersistenceUnitExtension
1670+
public class CustomTypeContributor implements TypeContributor {
1671+
1672+
@Override
1673+
public void contribute(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
1674+
// Registers the custom type so it can be used via @Type(value = BooleanYesNoType.class) on your entity property
1675+
typeContributions.getTypeConfiguration()
1676+
.getBasicTypeRegistry()
1677+
.register(new BooleanYesNoType(), "boolean_yes_no");
1678+
}
1679+
1680+
public static final class BooleanYesNoType implements UserType<Boolean> {
1681+
@Override
1682+
public int getSqlType() {
1683+
return SqlTypes.VARCHAR;
1684+
}
1685+
1686+
@Override
1687+
public Class<Boolean> returnedClass() {
1688+
return Boolean.class;
1689+
}
1690+
1691+
@Override
1692+
public Boolean nullSafeGet(ResultSet rs, int position, WrapperOptions options) throws SQLException {
1693+
String value = rs.getString(position);
1694+
1695+
if (value == null) {
1696+
return null;
1697+
}
1698+
1699+
return "Y".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value);
1700+
}
1701+
1702+
@Override
1703+
public void nullSafeSet(PreparedStatement st, Boolean value, int position, WrapperOptions options) throws SQLException {
1704+
if (value == null) {
1705+
st.setNull(position, SqlTypes.VARCHAR);
1706+
} else {
1707+
st.setString(position, value ? "Y" : "N");
1708+
}
1709+
}
1710+
1711+
@Override
1712+
public Boolean deepCopy(Boolean value) {
1713+
return value;
1714+
}
1715+
1716+
@Override
1717+
public boolean isMutable() {
1718+
return false;
1719+
}
1720+
}
1721+
}
1722+
----
1723+
15811724
[[interceptors]]
15821725
== Interceptors
15831726

0 commit comments

Comments
 (0)