Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

di il
32 risposte

32 Risposte - Pagina 2

  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    iBaffiPro ha scritto:


    Anche JPA ha lo stesso problema, è pesante e lento.
    No, un ORM completo come Hibernate non ha quel problema come ho descritto prima nello scenario di esempio.

    Hibernate sa cosa viene modificato in una entity e nelle entity referenziate.
    Spring Data JDBC invece non lo sa.
  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    Si però è ugualmente lento e scrivere query complesse è molto difficile.
  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    Oggi provo a togliere jdbc dal template ed usare jdbctemplate come da tuo consiglio. Mi sembra la soluzione più indicata per chi come me è poco esperto sia di database che di spring boot. Ti chiedo una curiosità, Spring Boot 2 con jdbctemplate gestisce gli id delle tabelle anche quando sono negativi oppure conviene editare lo scheda del database ed usare bigserial? Io non ho mai usato id negativi ma se jdbctemplate li gestisse permetterebbe a me di poter registrare più record in una tabella. Non che questo abbia molta utilità anche perché se avessi una tabella di quella dimensione non sarei qui a parlare con te ma siederei con una buona birra in riva al mare ma a mio avviso fale la pena sfruttare il framework fino all'osso.
  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    iBaffiPro ha scritto:


    Oggi provo a togliere jdbc dal template ed usare jdbctemplate come da tuo consiglio.
    Sinceramente non ho capito questa affermazione.

    Il template di JDBC di Spring sono due classi:
    • org.springframework.jdbc.core.JdbcTemplate
    • org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate

    (nota: non sono in relazione, la seconda non è una estensione della prima).

    Generalmente si tende ad usare la seconda perché permette di espandere i parametri nella forma :nome invece che con i ? usati da JdbcTemplate (e da JDBC puro in generale).

    iBaffiPro ha scritto:


    Spring Boot 2 con jdbctemplate gestisce gli id delle tabelle anche quando sono negativi oppure conviene editare lo scheda del database ed usare bigserial? Io non ho mai usato id negativi ma se jdbctemplate li gestisse permetterebbe a me di poter registrare più record in una tabella.
    Questo non c'entra nulla direttamente con i template di String. Nel senso che queste sono scelte che devi fare tu conoscendo il DB in uso.
    I template JDBC di Spring sono solo lo "strato" che è stato fatto per incapsulare tutta quella logica "prolissa/noiosa" di JDBC puro di cui hai già sicuramente ben idea. Le due classi di template mettono a disposizione metodi più semplici chiamati es. query, queryForList, queryForObject, update ecc.... a cui devi solamente passare: "dati" (parametri, class literal, ecc...) e/o "comportamenti" (es. una implementazione di RowMapper per "mappare" un record in un tuo oggetto). Per il resto, ci pensa appunto il template ad eseguire la query facendo tutta la trafila (noisosa) di operazioni con JDBC (e oltretutto facendola nel modo corretto/perfetto!)

    Riguardo i tipi delle colonne, chiave primaria, ecc... quindi puoi scegliere tutto quello che vuoi. Ai template non importa nulla, purché poi tu riesca a passare/estrarre bene ciò che serve attraverso quei metodi del template.
  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    Volevo dire che ho rimosso questo dal pom.xml:
    
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-data-jdbc</artifactId>
    </dependency>
    
    e che ho creato:
    Utente.java
    UtenteRepository.java
    UtenteRowMapper.java
    Ruolo.java
    RuoloRepository.java
    RuoloRowMapper.java

    dopo aver cancellato alcune classi dal precedente progetto come:
    AppRoleDAO.java
    AppUserDAO.java
    AppUserMapper.java

    Io uso i ? e non i :nome perché a me piace scrivere le classiche query come sono abituato a vedere e che posso anche testare su pgAdmin.
    A dir la verità ho già fatto tutto ma ho qualche problema. L'IDE mi dice che non vede alcune tabelle del database e la query per scrivere nel database non funziona. Ho anche creato una classe test apposita per testare tutte le varie query. Prima di chiederti un aiuto voglio pensarci un paio di giorni e capire da solo dove sbaglio.
    Comunque non ho capito esattamente quando devo usare BIGSERIAL e quando usare BIGINT, rifletto meglio su quello che hai scritto e ti faccio sapere, per ora grazie.
    Per ora di chiedo una cosa sola; sai se c'è un metodo semplice per condividere il mio progettino, in modo che tutti possano accedervi e vederlo?
    grazie
  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    iBaffiPro ha scritto:


    Volevo dire che ho rimosso questo dal pom.xml:
    
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-data-jdbc</artifactId>
    </dependency>
    
    Ok, questo è per Spring Data JDBC. Se vuoi solo usare i template JDBC di Spring lo starter minimo è spring-boot-starter-jdbc (non ha data- come vedi).

    iBaffiPro ha scritto:


    sai se c'è un metodo semplice per condividere il mio progettino, in modo che tutti possano accedervi e vederlo?
    Il minimo sarebbe una piattaforma di file-sharing (banalmente DropBox, per dire). Altrimenti, meglio, ci sono i vari noti hosting per il versionamento del codice (per Git c'è GitHub, Bitbucket ecc...).
  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    Ho riscritto il database precedente che mi piaceva poco ma senza fare grosse modifiche e tutte le query, dai test, sembrano funzionare, fatta eccezione per quella che tenta di inserire un nuovo utente nel DB.
    Ottengo questo errore:
    org.springframework.dao.InvalidDataAccessApiUsageException: The getKey method should only be used when a single key is returned. The current key entry contains multiple keys: [{id=4, nome=prova, password=password, abilitato=false}]
    La query è questa:
    
        public Utente crea(final Utente utente) {
            final String sql = "INSERT INTO utenti (nome, password, abilitato) VALUES (?, ?, ?);";
            KeyHolder holder = new GeneratedKeyHolder();
            jdbcTemplate.update(new PreparedStatementCreator() {
                @Override
                public PreparedStatement createPreparedStatement(Connection connection)
                        throws SQLException {
                    PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
                    ps.setString(1, utente.getNome());
                    ps.setString(2, utente.getPassword());
                    ps.setBoolean(3, utente.getAbilitato());
                    return ps;
                }
            }, holder);
            Long nuovoIdUtente = holder.getKey().longValue();
            utente.setId(nuovoIdUtente);
            return utente;
        }
    
    Il test questo:
    
    	@Test
    	public void creaUnUtente() {
    		Utente utente = new Utente(0L, "prova", "password", false);
    		Utente utenteSalvato = utenteRepository.crea(utente);
    		Utente nuovoUtente = utenteRepository.trovaUtente(utenteSalvato.getId());
    		assertEquals("prova", nuovoUtente.getNome());
    		assertEquals("password", nuovoUtente.getPassword());
    	}
    
    Ecco il database:
    schema.sql
    
    CREATE TABLE utenti (
        id BIGSERIAL PRIMARY KEY,
        nome VARCHAR(100) NOT NULL,
        password VARCHAR(255) NOT NULL,
        abilitato BOOLEAN NOT NULL,
        UNIQUE (nome)
    );
    
    CREATE TABLE ruoli (
        id SERIAL PRIMARY KEY,
        ruolo VARCHAR(100) NOT NULL,
        UNIQUE (ruolo)
    );
    
    CREATE TABLE id_utenti_id_ruoli (
        id BIGSERIAL PRIMARY KEY,
        id_utente BIGSERIAL NOT NULL,
        id_ruolo SERIAL NOT NULL,
        FOREIGN KEY (id_utente) REFERENCES utenti (id) ON DELETE CASCADE,
        FOREIGN KEY (id_ruolo) REFERENCES ruoli (id) ON DELETE CASCADE,
        UNIQUE (id_utente, id_ruolo)
    );
    
    CREATE TABLE persistent_logins (
        username VARCHAR(100) NOT NULL,
        series VARCHAR(64) PRIMARY KEY,
        token VARCHAR(64) NOT NULL,
        last_used TIMESTAMP NOT NULL
    );
    
    data.sql
    
    BEGIN TRANSACTION;
    
    INSERT INTO utenti (nome, password, abilitato)
    VALUES ('pro1', '$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', true);
    
    INSERT INTO utenti (nome, password, abilitato)
    VALUES ('amm1', '$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', true);
    
    INSERT INTO ruoli (ruolo) VALUES ('proprietario');
    INSERT INTO ruoli (ruolo) VALUES ('amministratore');
    INSERT INTO ruoli (ruolo) VALUES ('gestore');
    INSERT INTO ruoli (ruolo) VALUES ('organizzatore');
    INSERT INTO ruoli (ruolo) VALUES ('utente_semplice');
    
    INSERT INTO id_utenti_id_ruoli (id_utente, id_ruolo) VALUES (1, 1);
    INSERT INTO id_utenti_id_ruoli (id_utente, id_ruolo) VALUES (1, 2);
    INSERT INTO id_utenti_id_ruoli (id_utente, id_ruolo) VALUES (1, 3);
    INSERT INTO id_utenti_id_ruoli (id_utente, id_ruolo) VALUES (1, 4);
    INSERT INTO id_utenti_id_ruoli (id_utente, id_ruolo) VALUES (2, 2);
    INSERT INTO id_utenti_id_ruoli (id_utente, id_ruolo) VALUES (2, 3);
    INSERT INTO id_utenti_id_ruoli (id_utente, id_ruolo) VALUES (2, 4);
    
    COMMIT;
    
  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    iBaffiPro ha scritto:


    org.springframework.dao.InvalidDataAccessApiUsageException: The getKey method should only be used when a single key is returned. The current key entry contains multiple keys: [{id=4, nome=prova, password=password, abilitato=false}]
    
            Long nuovoIdUtente = holder.getKey().longValue();
    
    La questione è abbastanza semplice/logica. "Qualcuno" in tutta la trafila di strati dal JdbcTemplate in giù (quindi compresi driver JDBC e database) deve determinare quale è la colonna/e della chiave primaria. Sembra banale, TU sai che la chiave è il "id" ma evidentemente (per qualche motivo) non è in grado di determinarlo e ..... banalmente ti restituisce tutte le colonne come chiavi!!
    Ed è chiaro che un getKey() non basta.

    Non ho modo di provare ma banalmente si può risolvere sicuramente in vari modi. Prova quindi:

    Long nuovoIdUtente = ((Number) holder.getKeys().get("id")).longValue();

    In alternativa nel createPreparedStatement puoi provare a mettere:

    PreparedStatement ps = connection.prepareStatement(sql, new String[] { "id" });

    e quindi puoi continuare ad usare holder.getKey().longValue().


    Come ulteriore/ultima alternativa c'è il NamedParameterJdbcTemplate che ha il metodo:
    int update(String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHolder, String[] keyColumnNames)

    ma dovresti proprio cambiare approccio.
  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    Grazie mille!
    Funzionano entrambi gli approcci!
    Quale dei 2 a tuo avviso è il migliore?
  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    iBaffiPro ha scritto:


    Quale dei 2 a tuo avviso è il migliore?
    Direi proprio la seconda, specifichi in modo esplicito la colonna (e il RETURN_GENERATED_KEYS è già implicito) e puoi continuare ad usare getKey().longValue() invece di dover fare quel giro scomodo con il cast.
  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    Ho fatto bene a chiedere avevo scelto l'opzione meno preferibile. grazie infinite!
    Un'altra domanda, nella classe QualcosaRepository, dove scrivo le query relative alla tabella Qualcosa, conviene sempre mettere il return dentro un try/catch oppure no? Tu che linea di pensiero segui?
    
    @Repository
    public class RuoloRepository {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        @Transactional(readOnly=true)
        public List<Ruolo> trovaTutti() {
            return jdbcTemplate.query("SELECT * FROM ruoli;", new RuoloRowMapper());
        }
    
        @Transactional(readOnly=true)
        public List<String> trovaRuoliUtente(Long id) {
            final String sql;
            sql = "SELECT r.ruolo FROM id_utenti_id_ruoli ur, ruoli r WHERE ur.id_ruolo = r.id AND ur.id_utente = ?;";
            try {
                Object[] parametri = new Object[] {id};
                List<String> ruoli = jdbcTemplate.queryForList(sql, String.class, parametri);
                return ruoli;
            } catch (EmptyResultDataAccessException e) {
                return null;
            }
        }
    
        public Ruolo crea(final Ruolo ruolo) {
            final String sql = "INSERT INTO ruoli (ruolo) VALUES (?);";
            KeyHolder holder = new GeneratedKeyHolder();
            jdbcTemplate.update(new PreparedStatementCreator() {
                @Override
                public PreparedStatement createPreparedStatement(Connection connection)
                        throws SQLException {
                    PreparedStatement ps = connection.prepareStatement(sql, new String[] { "id" });
                    ps.setString(1, ruolo.getRuolo());
                    return ps;
                }
            }, holder);
            Long nuovoIdRuolo = holder.getKey().longValue();
            ruolo.setId(nuovoIdRuolo);
            return ruolo;
        }
    }
    
    
        @Transactional(readOnly=true)
        public List<String> trovaRuoliUtente(Long id) {
            final String sql;
            sql = "SELECT r.ruolo FROM id_utenti_id_ruoli ur, ruoli r WHERE ur.id_ruolo = r.id AND ur.id_utente = ?;";
            try {
                Object[] parametri = new Object[] {id};
                List<String> ruoli = jdbcTemplate.queryForList(sql, String.class, parametri);
                return ruoli;
            } catch (EmptyResultDataAccessException e) {
                return null;
            }
        }
    
    
        @Transactional(readOnly=true)
        public List<String> trovaRuoliUtente(Long id) {
            final String sql;
            Object[] parametri = new Object[] {id};
            sql = "SELECT r.ruolo FROM id_utenti_id_ruoli ur, ruoli r WHERE ur.id_ruolo = r.id AND ur.id_utente = ?;";
            List<String> ruoli = jdbcTemplate.queryForList(sql, String.class, parametri);
            return ruoli;
        }
    
    Come argomento dentro il metodo devo usare final oppure no?
    Dove trovo la documentazione ufficiale di jdbcTemplate?
  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    iBaffiPro ha scritto:


    Dove trovo la documentazione ufficiale di jdbcTemplate?
    La documentazione ufficiale di Spring Framework (Reference e API): https://spring.io/projects/spring-framework#lear
    E per qualunque altra versione, basta andare alla "radice" della documentazione dove ci sono tutte le versioni: https://docs.spring.io/spring-framework/docs


    P.S. al resto rispondo appena ho più tempo ..
  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    Non ho capito. Nell'elenco che trovo non vedo jdbcTemplate e non c'è neppure un tag cerca per trovare la risorsa.
    Ok, fai con comodo.
    Grazie mille
  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    iBaffiPro ha scritto:


    Nell'elenco che trovo non vedo jdbcTemplate e non c'è neppure un tag cerca per trovare la risorsa.
    La API Doc (il "javadoc"). E sì certo che c'è JdbcTemplate (usa il find del browser)..

    Purtroppo hanno generato il javadoc con il doclet "classico" (quello in uso fino al jdk 8 ) e non quello nuovo, che ha il campo di ricerca, come per il jdk 9 e oltre.
  • Re: Creare un sito dinamico in cui gli utenti possono registrarsi per usufruire di un servizio

    iBaffiPro ha scritto:


    conviene sempre mettere il return dentro un try/catch oppure no?
    
        @Transactional(readOnly=true)
        public List<String> trovaRuoliUtente(Long id) {
            final String sql;
            sql = "SELECT r.ruolo FROM id_utenti_id_ruoli ur, ruoli r WHERE ur.id_ruolo = r.id AND ur.id_utente = ?;";
            try {
                Object[] parametri = new Object[] {id};
                List<String> ruoli = jdbcTemplate.queryForList(sql, String.class, parametri);
                return ruoli;
            } catch (EmptyResultDataAccessException e) {
                return null;
            }
        }
    Il return va assolutamente bene, parlo del primo, quello nel try. La questione semmai è cosa fare/restituire in caso di eccezione: rilanci fuori la eccezione? lanci fuori una tua eccezione di più alto livello? restituisci una lista vuota? restituisci null?
    Sono scelte da valutare in base alla architettura generale che si vuole realizzare e anche in base al senso del metodo. Ti aspetti che ci sia sempre almeno un ruolo, quindi la assenza è per te un errore serio?

    iBaffiPro ha scritto:


    
    1    @Transactional(readOnly=true)
    2    public List<String> trovaRuoliUtente(Long id) {
    3        final String sql;
    4        Object[] parametri = new Object[] {id};
    5        sql = "SELECT r.ruolo FROM id_utenti_id_ruoli ur, ruoli r WHERE ur.id_ruolo = r.id AND ur.id_utente = ?;";
    6        List<String> ruoli = jdbcTemplate.queryForList(sql, String.class, parametri);
    7        return ruoli;
    8    }
    
    Come argomento dentro il metodo devo usare final oppure no?
    Se ti riferisci a quel final String sql; no, serve praticamente a poco/nulla. E' ragionevolmente improbabile/stupido che qualcuno possa andare ad assegnare altro a sql in mezzo tra le righe 5 e 6 ....
Devi accedere o registrarti per scrivere nel forum
32 risposte