Utilizzo di import.sql rispetto alle entità JPA persistenti

1

In Hibernate ORM & Spring Boot, durante la creazione di SessionFactory , è possibile eseguire istruzioni SQL DML per creare tabelle e / o inserire dati tramite file come import.sql definito in javax.persistence.hibernate.hbm2ddl.import_files proprietà. Come ho capito, l'utilizzo di questa funzione è particolarmente utile per l'inserimento di dati referenziali (ad es. Codici ISO come Paesi) sulla creazione dello schema ( create , create-drop o update ). L'uso della multi-tenancy di Hibernate significa anche che se i nuovi tenant vengono creati dinamicamente, verrà eseguito import.sql.

esempio import.sql:

INSERT INTO address_types (address_type_id, address_type) VALUES 
    (1, 'Home'), (2, 'Business') ON CONFLICT DO NOTHING;

AddressType.java:

@Entity
@Table(name = "address_types")
public class AddressType {
    @Id
    @SequenceGenerator(name = "address_types_id_seq", sequenceName = "address_types_id_seq")
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "address_types_id_seq")
    @Column(name = "address_type_id")
    private int id;
    @Column(name = "address_type", nullable = false, unique = true)
    private String type;

    // getters and setters ...
}

Tuttavia, ho notato che ci sono un paio di limitazioni nell'utilizzo di semplici istruzioni DML:

  1. Se si utilizzano sequenze rispetto a un tasto di autoincremento del database (tramite GenerationType.IDENTITY ) sulla colonna della chiave primaria, è facile inserire i dati referenziali e dimenticare di impostare il nuovo valore di sequenza o di specificare il numero di sequenza iniziale corretto nel Annotazione JPA. Questo errore può portare a DataIntegrityViolationException s se si inseriscono dati più tardi all'interno dell'applicazione:

    org.springframework.dao.DataIntegrityViolationException: could not execute batch; 
    SQL [/* insert com.mypackage.model.AddressType */ 
    insert into address_types (address_type_id, address_type) values (?, ?)]; 
    constraint [address_types_pkey]; 
    nested exception is org.hibernate.exception.ConstraintViolationException: could not execute batch
    

    La soluzione per questa limitazione sarebbe quella di impostare con cura il initialValue corretto nell'entità JPA:

    @SequenceGenerator(initialValue=2, name = "address_types_id_seq", sequenceName = "address_types_id_seq")'
    

    O per eseguire l'istruzione DML SQL SETVAL :

    SELECT SETVAL(address_types_id_seq, (SELECT MAX(address_type_id) FROM address_types))
    

    Entrambe le soluzioni di cui sopra sembrano non proprio ideali per me.

    Usando la prima correzione, devi tenere attentamente traccia del numero di entità inserite o lasciare uno spazio di sequenza extra (ad esempio, imposta initialValue su un numero maggiore).

    Con la seconda correzione, mentre è valido SQL DML, non sembra funzionare quando lo metto nel mio import.sql. Inoltre, con gli schemi multi-tenancy, potrebbe esserci qualche confusione su quale schema è in esecuzione l'istruzione SQL DML. È lo schema pubblico / dbo o è lo schema del tenant?

    Nel complesso, entrambi gli approcci sembrano soggetti a errori.

  2. Il refactoring dei nomi di entità JPA significa che anche import.sql deve essere aggiornato manualmente.

In alternativa, ho pensato perché non creare semplicemente i dati referenziali direttamente come entità Hibernate, persistendo quando l'applicazione si avvia per la prima volta? Uno di questi articoli che ho trovato suggerito esattamente che usando un Spring ApplicationListener :

@Component
public class ExampleLoader implements ApplicationListener<ContextRefreshedEvent>, Ordered {
    public int getOrder() {
        return 1;
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        //generate data
    }
}

Ci sono dei motivi contro questo approccio? O c'è una buona pratica per quanto riguarda l'impostazione dei dati referenziali?

    
posta David Yee 06.09.2017 - 09:24
fonte

0 risposte

Leggi altre domande sui tag