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
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@
@ValueGenerationType(generatedBy = SourceGeneration.class)
public @interface Source {
/**
* The source of timestamps. By default, the {@linkplain
* SourceType#VM virtual machine} is the source.
* The source of timestamps. By default, the
* {@linkplain SourceType#VM virtual machine}
* is the source.
*/
SourceType value() default SourceType.VM;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import org.hibernate.Incubating;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.id.insert.GetGeneratedKeysDelegate;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
import org.hibernate.id.insert.InsertReturningDelegate;
Expand Down Expand Up @@ -116,8 +115,8 @@ public interface OnExecutionGenerator extends Generator {
*/
@Incubating
default InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate(EntityPersister persister) {
final SessionFactoryImplementor factory = persister.getFactory();
final Dialect dialect = factory.getJdbcServices().getDialect();
final var factory = persister.getFactory();
final var dialect = factory.getJdbcServices().getDialect();
if ( dialect.supportsInsertReturningGeneratedKeys()
&& factory.getSessionFactoryOptions().isGetGeneratedKeysEnabled() ) {
return new GetGeneratedKeysDelegate( persister, false, INSERT );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
import java.lang.reflect.Member;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.Clock;
import java.time.Instant;
Expand All @@ -25,10 +23,7 @@
import java.util.Calendar;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;

import org.hibernate.SessionFactory;
import org.hibernate.annotations.CreationTimestamp;
Expand All @@ -39,7 +34,6 @@
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.engine.jdbc.spi.StatementPreparer;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.generator.EventType;
Expand Down Expand Up @@ -88,108 +82,9 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE

private final EnumSet<EventType> eventTypes;
private final JavaType<Object> propertyType;
private final CurrentTimestampGeneratorDelegate delegate;
private final GeneratorDelegate delegate;

private static final Map<Class<?>, BiFunction<@Nullable Clock, Integer, CurrentTimestampGeneratorDelegate>> GENERATOR_PRODUCERS = new HashMap<>();
private static final Map<Key, CurrentTimestampGeneratorDelegate> GENERATOR_DELEGATES = new ConcurrentHashMap<>();

static {
GENERATOR_PRODUCERS.put(
Date.class,
(baseClock, precision) -> {
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 3 );
return () -> new Date( clock.millis() );
}
);
GENERATOR_PRODUCERS.put(
Calendar.class,
(baseClock, precision) -> {
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 3 );
return () -> {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis( clock.millis() );
return calendar;
};
}
);
GENERATOR_PRODUCERS.put(
java.sql.Date.class,
(baseClock, precision) -> () -> new java.sql.Date( baseClock == null ? System.currentTimeMillis() : baseClock.millis() )
);

GENERATOR_PRODUCERS.put(
Time.class,
(baseClock, precision) -> {
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 3 );
return () -> new Time( clock.millis() );
}
);
GENERATOR_PRODUCERS.put(
Timestamp.class,
(baseClock, precision) -> {
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 9 );
return () -> Timestamp.from( clock.instant() );
}
);
GENERATOR_PRODUCERS.put(
Instant.class,
(baseClock, precision) -> {
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 9 );
return clock::instant;
}
);
GENERATOR_PRODUCERS.put(
LocalDate.class,
(baseClock, precision) -> () -> LocalDate.now( baseClock == null ? Clock.systemDefaultZone() : baseClock )
);
GENERATOR_PRODUCERS.put(
LocalDateTime.class,
(baseClock, precision) -> {
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 9 );
return () -> LocalDateTime.now( clock );
}
);
GENERATOR_PRODUCERS.put(
LocalTime.class,
(baseClock, precision) -> {
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 9 );
return () -> LocalTime.now( clock );
}
);
GENERATOR_PRODUCERS.put(
MonthDay.class,
(baseClock, precision) -> () -> MonthDay.now( baseClock == null ? Clock.systemDefaultZone() : baseClock )
);
GENERATOR_PRODUCERS.put(
OffsetDateTime.class,
(baseClock, precision) -> {
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 9 );
return () -> OffsetDateTime.now( clock );
}
);
GENERATOR_PRODUCERS.put(
OffsetTime.class,
(baseClock, precision) -> {
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 9 );
return () -> OffsetTime.now( clock );
}
);
GENERATOR_PRODUCERS.put(
Year.class,
(baseClock, precision) -> () -> Year.now( baseClock == null ? Clock.systemDefaultZone() : baseClock )
);
GENERATOR_PRODUCERS.put(
YearMonth.class,
(baseClock, precision) -> () -> YearMonth.now( baseClock == null ? Clock.systemDefaultZone() : baseClock )
);
GENERATOR_PRODUCERS.put(
ZonedDateTime.class,
(baseClock, precision) -> {
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 9 );
return () -> ZonedDateTime.now( clock );
}
);
}
private static final ConcurrentHashMap<Key, GeneratorDelegate> GENERATOR_DELEGATES = new ConcurrentHashMap<>();

public CurrentTimestampGeneration(CurrentTimestamp annotation, Member member, GeneratorCreationContext context) {
delegate = getGeneratorDelegate( annotation.source(), member, context );
Expand All @@ -209,43 +104,43 @@ public CurrentTimestampGeneration(UpdateTimestamp annotation, Member member, Gen
propertyType = getPropertyType( context );
}

private static CurrentTimestampGeneratorDelegate getGeneratorDelegate(
private static GeneratorDelegate getGeneratorDelegate(
SourceType source,
Member member,
GeneratorCreationContext context) {
return getGeneratorDelegate( source, ReflectHelper.getPropertyType( member ), context );
}

static CurrentTimestampGeneratorDelegate getGeneratorDelegate(
static GeneratorDelegate getGeneratorDelegate(
SourceType source,
Class<?> propertyType,
GeneratorCreationContext context) {
return switch (source) {
case DB -> null;
case VM -> {
// Generator is only used for in-VM generation
final Key key = new Key( propertyType, getBaseClock( context ), getPrecision( context ) );
final var delegate = GENERATOR_DELEGATES.get( key );
if ( delegate != null ) {
yield delegate;
}
else {
final var producer = GENERATOR_PRODUCERS.get( key.clazz );
if ( producer == null ) {
yield null;
}
else {
final var generatorDelegate = producer.apply( key.clock, key.precision );
final var old = GENERATOR_DELEGATES.putIfAbsent( key, generatorDelegate );
yield old != null ? old : generatorDelegate;
}
}
}
case VM -> getGeneratorDelegate( propertyType, getBaseClock( context ), getPrecision( context ) );
};
}

private static GeneratorDelegate getGeneratorDelegate(Class<?> propertyType, Clock baseClock, int precision) {
final var key = new Key( propertyType, baseClock, precision );
final var delegate = GENERATOR_DELEGATES.get( key );
if ( delegate != null ) {
return delegate;
}
else {
final var generatorDelegate = generatorDelegate( propertyType, baseClock, precision );
if ( generatorDelegate == null ) {
return null;
}
else {
final var old = GENERATOR_DELEGATES.putIfAbsent( key, generatorDelegate );
return old != null ? old : generatorDelegate;
}
}
}

private static int getPrecision(GeneratorCreationContext context) {
final BasicValue basicValue = (BasicValue) context.getProperty().getValue();
final var basicValue = (BasicValue) context.getProperty().getValue();
final Size size =
basicValue.getColumns().get( 0 )
.getColumnSize( context.getDatabase().getDialect(),
Expand Down Expand Up @@ -305,16 +200,8 @@ public String[] getReferencedColumnValues(Dialect dialect) {
return new String[] { dialect.currentTimestamp() };
}

interface CurrentTimestampGeneratorDelegate {
// Left out the Generator params, they're not used anyway. Since this is purely internal, this can be changed if needed
Object generate();
}

private record Key(Class<?> clazz, @Nullable Clock clock, int precision) {
}

static Timestamp getCurrentTimestamp(SharedSessionContractImplementor session) {
final Dialect dialect = session.getJdbcServices().getJdbcEnvironment().getDialect();
final var dialect = session.getJdbcServices().getJdbcEnvironment().getDialect();
return getCurrentTimestampFromDatabase(
dialect.getCurrentTimestampSelectString(),
dialect.isCurrentTimestampSelectStringCallable(),
Expand All @@ -326,8 +213,8 @@ static Timestamp getCurrentTimestampFromDatabase(
String timestampSelectString,
boolean callable,
SharedSessionContractImplementor session) {
final JdbcCoordinator coordinator = session.getJdbcCoordinator();
final StatementPreparer statementPreparer = coordinator.getStatementPreparer();
final var coordinator = session.getJdbcCoordinator();
final var statementPreparer = coordinator.getStatementPreparer();
PreparedStatement statement = null;
try {
statement = statementPreparer.prepareStatement( timestampSelectString, callable );
Expand Down Expand Up @@ -356,16 +243,92 @@ static Timestamp getCurrentTimestampFromDatabase(

static Timestamp extractResult(PreparedStatement statement, JdbcCoordinator coordinator, String sql)
throws SQLException {
final ResultSet resultSet = coordinator.getResultSetReturn().extract( statement, sql );
final var resultSet = coordinator.getResultSetReturn().extract( statement, sql );
resultSet.next();
return resultSet.getTimestamp( 1 );
}

static Timestamp extractCalledResult(PreparedStatement statement, JdbcCoordinator coordinator, String sql)
throws SQLException {
final CallableStatement callable = (CallableStatement) statement;
final var callable = (CallableStatement) statement;
callable.registerOutParameter( 1, TIMESTAMP );
coordinator.getResultSetReturn().execute( callable, sql );
return callable.getTimestamp( 1 );
}

@FunctionalInterface
interface GeneratorDelegate {
// Left out the Generator params, they're not used anyway.
// Since this is purely internal, this can be changed if needed.
Object generate();
}

private record Key(Class<?> clazz, @Nullable Clock clock, int precision) {
}

private static GeneratorDelegate generatorDelegate(Class<?> clazz, @Nullable Clock baseClock, int precision) {
if ( clazz == Instant.class ) {
final var clock = ClockHelper.forPrecision( baseClock, precision, 9 );
return clock::instant;
}
else if ( clazz == LocalDateTime.class ) {
final var clock = ClockHelper.forPrecision( baseClock, precision, 9 );
return () -> LocalDateTime.now( clock );
}
else if ( clazz == LocalDate.class ) {
return () -> LocalDate.now( baseClock == null ? Clock.systemDefaultZone() : baseClock );
}
else if ( clazz == LocalTime.class ) {
final var clock = ClockHelper.forPrecision( baseClock, precision, 9 );
return () -> LocalTime.now( clock );
}
else if ( clazz == OffsetDateTime.class ) {
final var clock = ClockHelper.forPrecision( baseClock, precision, 9 );
return () -> OffsetDateTime.now( clock );
}
else if ( clazz == OffsetTime.class ) {
final var clock = ClockHelper.forPrecision( baseClock, precision, 9 );
return () -> OffsetTime.now( clock );
}
else if ( clazz == ZonedDateTime.class ) {
final var clock = ClockHelper.forPrecision( baseClock, precision, 9 );
return () -> ZonedDateTime.now( clock );
}
else if ( clazz == Year.class ) {
return () -> Year.now( baseClock == null ? Clock.systemDefaultZone() : baseClock );
}
else if ( clazz == YearMonth.class ) {
return () -> YearMonth.now( baseClock == null ? Clock.systemDefaultZone() : baseClock );
}
else if ( clazz == MonthDay.class ) {
return () -> MonthDay.now( baseClock == null ? Clock.systemDefaultZone() : baseClock );
}
// DEPRECATED:
else if ( clazz == Date.class ) {
final var clock = ClockHelper.forPrecision( baseClock, precision, 3 );
return () -> new Date( clock.millis() );
}
else if ( clazz == Calendar.class ) {
final var clock = ClockHelper.forPrecision( baseClock, precision, 3 );
return () -> {
var calendar = Calendar.getInstance();
calendar.setTimeInMillis( clock.millis() );
return calendar;
};
}
else if ( clazz == java.sql.Timestamp.class ) {
final var clock = ClockHelper.forPrecision( baseClock, precision, 9 );
return () -> java.sql.Timestamp.from( clock.instant() );
}
else if ( clazz == java.sql.Date.class ) {
return () -> new java.sql.Date( baseClock == null ? System.currentTimeMillis() : baseClock.millis() );
}
else if ( clazz == java.sql.Time.class ) {
final var clock = ClockHelper.forPrecision( baseClock, precision, 3 );
return () -> new java.sql.Time( clock.millis() );
}
else {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.EventType;
import org.hibernate.generator.OnExecutionGenerator;
import org.hibernate.persister.entity.EntityPersister;

import java.util.EnumSet;

Expand Down Expand Up @@ -75,7 +74,7 @@ public boolean writePropertyValue() {
public boolean generatedOnExecution(Object entity, SharedSessionContractImplementor session) {
if ( writable ) {
// When this is the identifier generator and writable is true, allow pre-assigned identifiers
final EntityPersister entityPersister = session.getEntityPersister( null, entity );
final var entityPersister = session.getEntityPersister( null, entity );
return entityPersister.getGenerator() != this
|| entityPersister.getIdentifier( entity, session ) == null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
public class SourceGeneration implements BeforeExecutionGenerator {

private final JavaType<?> propertyType;
private final CurrentTimestampGeneration.CurrentTimestampGeneratorDelegate valueGenerator;
private final CurrentTimestampGeneration.GeneratorDelegate valueGenerator;

public SourceGeneration(Source annotation, Member member, GeneratorCreationContext context) {
this( annotation.value(), context.getProperty().getType().getReturnedClass(), context );
Expand Down
Loading