Skip to content

Commit 47d8a63

Browse files
committed
HHH-16638 allow @DialectOverride for @SQLInsert and friends
1 parent 62c05ea commit 47d8a63

File tree

3 files changed

+185
-4
lines changed

3 files changed

+185
-4
lines changed

hibernate-core/src/main/java/org/hibernate/annotations/DialectOverride.java

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,12 +380,108 @@ public interface DialectOverride {
380380

381381
org.hibernate.annotations.SQLSelect override();
382382
}
383-
@Target({METHOD, FIELD})
383+
@Target({METHOD, FIELD, TYPE})
384384
@Retention(RUNTIME)
385385
@interface SQLSelects {
386386
SQLSelect[] value();
387387
}
388388

389+
/**
390+
* Specializes a {@link org.hibernate.annotations.SQLInsert}
391+
* in a certain dialect.
392+
*/
393+
@Target({METHOD, FIELD, TYPE})
394+
@Retention(RUNTIME)
395+
@Repeatable(SQLInserts.class)
396+
@OverridesAnnotation(org.hibernate.annotations.SQLInsert.class)
397+
@interface SQLInsert {
398+
/**
399+
* The {@link Dialect} in which this override applies.
400+
*/
401+
Class<? extends Dialect> dialect();
402+
Version before() default @Version(major = MAX_VALUE);
403+
Version sameOrAfter() default @Version(major = MIN_VALUE);
404+
405+
org.hibernate.annotations.SQLInsert override();
406+
}
407+
@Target({METHOD, FIELD, TYPE})
408+
@Retention(RUNTIME)
409+
@interface SQLInserts {
410+
SQLInsert[] value();
411+
}
412+
413+
/**
414+
* Specializes a {@link org.hibernate.annotations.SQLUpdate}
415+
* in a certain dialect.
416+
*/
417+
@Target({METHOD, FIELD, TYPE})
418+
@Retention(RUNTIME)
419+
@Repeatable(SQLUpdates.class)
420+
@OverridesAnnotation(org.hibernate.annotations.SQLUpdate.class)
421+
@interface SQLUpdate {
422+
/**
423+
* The {@link Dialect} in which this override applies.
424+
*/
425+
Class<? extends Dialect> dialect();
426+
Version before() default @Version(major = MAX_VALUE);
427+
Version sameOrAfter() default @Version(major = MIN_VALUE);
428+
429+
org.hibernate.annotations.SQLUpdate override();
430+
}
431+
@Target({METHOD, FIELD, TYPE})
432+
@Retention(RUNTIME)
433+
@interface SQLUpdates {
434+
SQLUpdate[] value();
435+
}
436+
437+
/**
438+
* Specializes a {@link org.hibernate.annotations.SQLDelete}
439+
* in a certain dialect.
440+
*/
441+
@Target({METHOD, FIELD, TYPE})
442+
@Retention(RUNTIME)
443+
@Repeatable(SQLDeletes.class)
444+
@OverridesAnnotation(org.hibernate.annotations.SQLDelete.class)
445+
@interface SQLDelete {
446+
/**
447+
* The {@link Dialect} in which this override applies.
448+
*/
449+
Class<? extends Dialect> dialect();
450+
Version before() default @Version(major = MAX_VALUE);
451+
Version sameOrAfter() default @Version(major = MIN_VALUE);
452+
453+
org.hibernate.annotations.SQLDelete override();
454+
}
455+
@Target({METHOD, FIELD, TYPE})
456+
@Retention(RUNTIME)
457+
@interface SQLDeletes {
458+
SQLDelete[] value();
459+
}
460+
461+
/**
462+
* Specializes a {@link org.hibernate.annotations.SQLDeleteAll}
463+
* in a certain dialect.
464+
*/
465+
@Target({METHOD, FIELD, TYPE})
466+
@Retention(RUNTIME)
467+
@Repeatable(SQLDeleteAlls.class)
468+
@OverridesAnnotation(org.hibernate.annotations.SQLDeleteAll.class)
469+
@interface SQLDeleteAll {
470+
/**
471+
* The {@link Dialect} in which this override applies.
472+
*/
473+
Class<? extends Dialect> dialect();
474+
Version before() default @Version(major = MAX_VALUE);
475+
Version sameOrAfter() default @Version(major = MIN_VALUE);
476+
477+
org.hibernate.annotations.SQLDeleteAll override();
478+
}
479+
@Target({METHOD, FIELD, TYPE})
480+
@Retention(RUNTIME)
481+
@interface SQLDeleteAlls {
482+
SQLDeleteAll[] value();
483+
}
484+
389485
/**
390486
* Marks an annotation type as a dialect-specific override for
391487
* some other annotation type.

hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,8 +1252,6 @@ private boolean isForceDiscriminatorInSelects() {
12521252

12531253
private void bindCustomSql() {
12541254
//TODO: tolerate non-empty table() member here if it explicitly names the main table
1255-
//TODO: would be nice to add these guys to @DialectOverride, but getOverridableAnnotation()
1256-
// does not yet handle repeatable annotations
12571255

12581256
final SQLInsert sqlInsert = findMatchingSqlAnnotation( "", SQLInsert.class, SQLInserts.class );
12591257
if ( sqlInsert != null ) {
@@ -1950,12 +1948,13 @@ private <T extends Annotation,R extends Annotation> T findMatchingSqlAnnotation(
19501948
String tableName,
19511949
Class<T> annotationType,
19521950
Class<R> repeatableType) {
1953-
final T sqlAnnotation = annotatedClass.getAnnotation( annotationType );
1951+
final T sqlAnnotation = getOverridableAnnotation( annotatedClass, annotationType, context );
19541952
if ( sqlAnnotation != null ) {
19551953
if ( tableName.equals( tableMember( annotationType, sqlAnnotation ) ) ) {
19561954
return sqlAnnotation;
19571955
}
19581956
}
1957+
//TODO: getOverridableAnnotation() does not yet handle @Repeatable annotations
19591958
final R repeatable = annotatedClass.getAnnotation(repeatableType);
19601959
if ( repeatable != null ) {
19611960
for ( Annotation current : valueMember( repeatableType, repeatable ) ) {
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package org.hibernate.orm.test.customsql;
2+
3+
import jakarta.persistence.Entity;
4+
import jakarta.persistence.GeneratedValue;
5+
import jakarta.persistence.GenerationType;
6+
import jakarta.persistence.Id;
7+
import jakarta.persistence.Table;
8+
import org.hibernate.annotations.DialectOverride;
9+
import org.hibernate.annotations.Generated;
10+
import org.hibernate.annotations.SQLInsert;
11+
import org.hibernate.annotations.SQLUpdate;
12+
import org.hibernate.dialect.H2Dialect;
13+
import org.hibernate.dialect.MySQLDialect;
14+
import org.hibernate.dialect.PostgreSQLDialect;
15+
import org.hibernate.dialect.SQLServerDialect;
16+
import org.hibernate.testing.orm.junit.DomainModel;
17+
import org.hibernate.testing.orm.junit.RequiresDialect;
18+
import org.hibernate.testing.orm.junit.SessionFactory;
19+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
20+
import org.junit.jupiter.api.Test;
21+
22+
import static org.junit.jupiter.api.Assertions.assertEquals;
23+
import static org.junit.jupiter.api.Assertions.assertNotEquals;
24+
import static org.junit.jupiter.api.Assertions.assertNotNull;
25+
26+
@SessionFactory
27+
@DomainModel(annotatedClasses = CustomSqlOverrideTest.Custom.class)
28+
@RequiresDialect(H2Dialect.class)
29+
@RequiresDialect(MySQLDialect.class)
30+
@RequiresDialect(PostgreSQLDialect.class)
31+
@RequiresDialect(SQLServerDialect.class)
32+
public class CustomSqlOverrideTest {
33+
@Test
34+
public void testCustomSql(SessionFactoryScope scope) {
35+
Custom c = new Custom();
36+
scope.inTransaction(s->{
37+
s.persist(c);
38+
c.whatever = "old value";
39+
s.flush();
40+
assertNotNull(c.id);
41+
assertNotNull(c.uid);
42+
s.clear();
43+
Custom cc = s.find(Custom.class, c.id);
44+
assertNotNull(cc.id);
45+
assertNotNull(cc.uid);
46+
assertEquals("old value", cc.whatever);
47+
cc.whatever = "new value";
48+
s.flush();
49+
s.clear();
50+
Custom ccc = s.find(Custom.class, c.id);
51+
assertNotNull(cc.id);
52+
assertNotNull(cc.uid);
53+
assertEquals("new value", ccc.whatever);
54+
assertEquals(cc.id, ccc.id);
55+
assertNotEquals(cc.uid, ccc.uid);
56+
});
57+
}
58+
@Entity
59+
@Table(name = "CustomTable")
60+
// @SQLInsert(sql="")
61+
@DialectOverride.SQLInsert(dialect = H2Dialect.class,
62+
override = @SQLInsert(sql="insert into CustomTable (uid,whatever) values (random_uuid(),?)"))
63+
@DialectOverride.SQLInsert(dialect = MySQLDialect.class,
64+
override = @SQLInsert(sql="insert into CustomTable (uid,whatever) values (uuid(),?)"))
65+
@DialectOverride.SQLInsert(dialect = PostgreSQLDialect.class,
66+
override = @SQLInsert(sql="insert into CustomTable (uid,whatever) values (gen_random_uuid(),?)"))
67+
@DialectOverride.SQLInsert(dialect = SQLServerDialect.class,
68+
override = @SQLInsert(sql="insert into CustomTable (uid,whatever) values (newid(),?)"))
69+
// @SQLUpdate(sql="")
70+
@DialectOverride.SQLUpdate(dialect = H2Dialect.class,
71+
override = @SQLUpdate(sql="update CustomTable set uid = random_uuid(), whatever = ? where id = ?"))
72+
@DialectOverride.SQLUpdate(dialect = MySQLDialect.class,
73+
override = @SQLUpdate(sql="update CustomTable set uid = uuid(), whatever = ? where id = ?"))
74+
@DialectOverride.SQLUpdate(dialect = PostgreSQLDialect.class,
75+
override = @SQLUpdate(sql="update CustomTable set uid = gen_random_uuid(), whatever = ? where id = ?"))
76+
@DialectOverride.SQLUpdate(dialect = SQLServerDialect.class,
77+
override = @SQLUpdate(sql="update CustomTable set uid = newid(), whatever = ? where id = ?"))
78+
static class Custom {
79+
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
80+
Long id;
81+
@Generated
82+
String uid;
83+
String whatever;
84+
}
85+
}
86+

0 commit comments

Comments
 (0)