2009-05-12 11 views
19

Ho una tabella che mappa String-> Intero.Compilare un enum con i valori del database

Invece di creare un enum in modo statico, desidero popolare l'enumerazione con i valori di un database. È possibile ?

Così, piuttosto che delcaring questo modo statico:

public enum Size { SMALL(0), MEDIUM(1), LARGE(2), SUPERSIZE(3) }; 

voglio creare questa enum dinamicamente dato che i numeri {0,1,2,3} sono fondamentalmente casuale (perché sono generati automaticamente dal AUTOINCREMENT della banca dati colonna).

+0

Duplicate: http://stackoverflow.com/questions/725043/dynamic-enum-in-c – aleemb

+4

Solo che era per C#, non Java, e noi tutti sappiamo che si può fare di più con Java :-) – paxdiablo

risposta

25

No. Le enumerazioni sono sempre fisse in fase di compilazione. L'unico modo per farlo sarebbe generare dinamicamente il codice byte pertinente.

Detto questo, probabilmente si dovrebbe capire che gli aspetti di un enum si sta effettivamente interessato a. Presumibilmente non eri vogliono utilizzare un'istruzione switch su di loro, in quanto ciò significherebbe codice statico e non lo fai conoscere i valori staticamente ... allo stesso modo qualsiasi altro riferimento nel codice.

Se davvero si vuole solo una mappa String-Integer, si può semplicemente utilizzare un Map<String, Integer>, che si popola in fase di esecuzione, e il gioco è fatto. Se si desiderano le funzionalità EnumSet, esse sarebbero in qualche modo più complicate da riprodurre con la stessa efficienza, ma potrebbe essere fattibile con qualche sforzo.

Quindi, prima di continuare a pensare all'implementazione, ti suggerisco di capire quali sono le tue reali esigenze.

(EDIT: Ho presumo che questo enum è completamente dinamica, vale a dire che non si conoscono i nomi o anche quanti valori ci sono Se la serie di nomi che è fisso e solo bisogno. recuperare l'ID dal database, è molto diverso - vedi Andreas' answer.)

+1

Guarda BeanShell se vuoi generare dinamicamente il codice e compilarlo in fase di runtime. –

2

Le enumerazioni non sono dinamiche, quindi la risposta breve è che non è possibile farlo.

hanno anche uno sguardo a Stack Overflow questione Dynamic enum in C#.

0

In tutte le lingue, so che le enumerazioni sono statiche. Il compilatore può fare alcune ottimizzazioni su di loro. Quindi la risposta breve è no, non puoi.

La domanda è perché si desidera utilizzare un enum in questo modo. Cosa ti aspetti? O, in altre parole, perché non usare una collezione, invece?

+0

Codice legacy, in cui è presente un enum in tutto il luogo che ora vogliamo caricare dinamicamente dal database. Salva una discreta quantità di refactoring se puoi semplicemente caricare l'enum al volo ... –

21

Questo è un po 'complicato, dal momento che la popolazione di tali valori si verifica in fase di caricamento in classe. Quindi avrai bisogno di un accesso statico a una connessione al database.

Per quanto apprezzi le sue risposte, penso che Jon Skeet potrebbe sbagliarsi questa volta.

Date un'occhiata a questo:

public enum DbEnum { 
    FIRST(getFromDb("FIRST")), SECOND(getFromDb("second")); 

    private static int getFromDb(String s) { 
     PreparedStatement statement = null; 
     ResultSet rs = null; 
     try { 
      Connection c = ConnectionFactory.getInstance().getConnection(); 
      statement = c.prepareStatement("select id from Test where name=?"); 
      statement.setString(1, s); 
      rs = statement.executeQuery(); 
      return rs.getInt(1); 

     } 
     catch (SQLException e) { 
      throw new RuntimeException("error loading enum value for "+s,e); 
     } 
     finally { 
      try { 
       rs.close(); 
       statement.close(); 
      } catch (SQLException e) { 
       //ignore 
      } 
     } 
     throw new IllegalStateException("have no database"); 
    } 

    final int value; 

    DbEnum(int value) { 
     this.value = value; 
    } 
} 
+3

Supponi di sapere a * tempo di compilazione * quanti valori ci saranno e quali dovrebbero essere chiamati. Se è così, allora va bene - ma non vedo nulla nella domanda che lo suggerisca. (Penso che la tua query dovrebbe selezionare anche l'ID, piuttosto che solo 1 :) –

+2

Una risposta informativa che ci ricorda gli aspetti orientati agli oggetti dell'enumerazione - grazie, Andreas. –

+0

(risposta tardiva, lo so) Si potrebbe fare questo DRYer usando la riflessione per popolare i valori, penso. Quindi potresti semplicemente avere FIRST() e cercare il nome nel codice di inizializzazione? – Rich

1

È necessario replicare nel codice ciò che è nel database (o viceversa). Vedi questo question per alcuni buoni consigli.

5

Miglioramento in what Andreas did, è possibile caricare il contenuto del database in una mappa per ridurre il numero di connessioni al database necessarie.

public enum DbEnum { 
    FIRST(getFromDb("FIRST")), 
    SECOND(getFromDb("second")); 

    private Map<String,Integer> map; 
    private static int getFromDB(String s) 
    { 
     if (map == null) 
     { 
      map = new HashMap<String,Integer>(); 
      // Continue with database code but get everything and 
      // then populate the map with key-value pairs. 
      return map.get(s); 
     } 
     else { 
      return map.get(s); } 
    } 
} 
+0

A questo punto è bene citare questa idea. Tuttavia, è chiaro che questo codice funziona in modo orribile in qualsiasi ambiente di produzione realistico (vale a dire multi-thread). Se l'enum viene utilizzato ampiamente all'interno dell'app, questo codice di inizializzazione è destinato a restituire valori assolutamente inaffidabili. –

Problemi correlati