JPA funziona con H2 ma non con MySQL

di il
20 risposte

20 Risposte - Pagina 2

  • Re: JPA funziona con H2 ma non con MySQL

    L'ho letto ma non ci capisco nulla. Ho già scritto qualche query in MySQL tempo addietro e non mi sono mai dovuto preoccupare di byte. Sono completamente disorientato. Avevo anche letto che con Spring Data JPA è possibile realizzare applicazioni indipendenti dal tipo di database ma questo non è vero perché questo codice è scritto in MySQL:
    
    DROP DATABASE IF EXISTS test;
    CREATE DATABASE test 
    CHARACTER SET = 'utf8'
    COLLATE = 'utf8_general_ci';
    
    se non scrivo questo pezzo in MySQL la webapp non funziona. In rete si trovano delle specifiche da inserire nel .properties ma non funzionano.
    Ho postato tutto in qualche messaggio addietro.
  • Re: JPA funziona con H2 ma non con MySQL

    giannino1995 ha scritto:


    Avevo anche letto che con Spring Data JPA è possibile realizzare applicazioni indipendenti dal tipo di database ma questo non è vero
    Certo che è vero. Perlomeno a livello applicativo si può stare ad un livello molto astratto da un DBMS specifico ... a patto che a) non usi delle native query da JPA; b) non infarcisci le entity troppo di cose "specifiche" del DBMS.

    Per il resto, tutto ciò che sta eventualmente "esterno" (es. script con Flyway) POSSONO benissimo (e generalmente devono) essere specifici del DBMS.

    giannino1995 ha scritto:


    se non scrivo questo pezzo in MySQL la webapp non funziona.
    In quella discussione, c'è una semplice frase:

    One option you have is to just place lower limit on your VARCHAR fields.

    Nel springboot-jpa-demo la entity User è poco descritta con le annotation, c'è giusto solo il minimo, per il resto prende molti "default" (come la lunghezza).
    Bene, riduci la dimensione della email (a 100 è già tanto).
    	@Column(nullable=false, unique=true, length=100)
    	private String email;
    E ... ti funzionerà senza fare magheggi ...
  • Re: JPA funziona con H2 ma non con MySQL

    Grazie mille! Bravissimo!
    C'è però un dettaglio che non mi è chiaro. Nell'altro forum leggo:
    i think your calculation is a bit wrong here. mysql uses 1 or 2 extra bytes to record the values length: 1 byte if the column's max length is 255 bytes or less, 2 if it's longer than 255 bytes. the utf8_general_ci encoding needs 3 bytes per character so varchar(20) uses 61 bytes, varchar(500) uses 1502 bytes in total 1563 bytes
    Io nel frattempo ho impostato il limite a 1000 byte ma se lancio:
    
    @Column(nullable=false, unique=true, length=250)
    private String email;
    
    Spring Boot e anche Workbench funzionano uguale per cui mi chiedo se questo commento sia corretto.
    Perché sul mio DBMS MySQL con motore MyISAM non ho questi 1 o 2 byte extra?
    Non che la cosa mi dispiaccia eh! Sia chiaro, anzi mi sembra una cosa molto più pulita concettualmente.
  • Re: JPA funziona con H2 ma non con MySQL

    giannino1995 ha scritto:


    Io nel frattempo ho impostato il limite a 1000 byte ma se lancio:
    
    @Column(nullable=false, unique=true, length=250)
    private String email;
    
    Spring Boot e anche Workbench funzionano uguale per cui mi chiedo se questo commento sia corretto.
    Perché sul mio DBMS MySQL con motore MyISAM non ho questi 1 o 2 byte extra?
    In questo momento, onestamente, non saprei risponderti ... dovrei andare a leggere della documentazione.

    Ma poi comunque, scusa .... che questioni ti stai facendo?? Di byte, limiti, ecc...? Che in questo momento probabilmente non dovresti nemmeno farti.

    Stai studiando Spring Boot (con non so bene quali risultati ...), JPA non lo conosci (forse hai solo intuito qualcosa vedendo quei @Column), Spring base nemmeno e su SQL/MySQL probabilmente non tanto ....
  • Re: JPA funziona con H2 ma non con MySQL

    Ciao Andbin, scusa se ritorno sull'argomento, pensavo di aver capito tutto ma in realtà non era così. Ho trovato una seconda opzione per far funzionare a dovere l'applicazione. Invece di creare un metodo per caricare nel DBMS i record in fase di test (vedi Test_00_createRecordBase()) ho modificato l'assegnazione dell'id nel DBMS (vedi strategy=GenerationType.IDENTITY). Cambiando il tipo di assegnazione dell'id (IDENTITY, SEQUENCE, AUTO, TABLE), il metodo di caricamento (Test_00_createRecordBase() oppure data.sql) e la tipologia di database (H2 o MySQL) sono usciti fuori 16 casi.

    CASI ANALIZZATI

    A) @GeneratedValue(strategy=GenerationType.IDENTITY)

    1) H2 con file data.sql >> OK! (6 su 6 sono ok)
    2) H2 con metodo in java Test_00_createRecordBase() >> OK! (7 su 7 sono ok)
    3) MySQL con file data.sql >> OK! (6 su 6 sono ok)
    4) MySQL con metodo in java Test_00_createRecordBase() >> OK! (7 su 7 sono ok)

    B) @GeneratedValue(strategy=GenerationType.AUTO)

    1) H2 con file data.sql >> NO! (solo 5 su 6 ok sono ok)
    2) H2 con metodo in java Test_00_createRecordBase() >> OK! (7 su 7 sono ok)
    3) MySQL con file data.sql >> NO! (solo 5 su 6 sono ok)
    4) MySQL con metodo in java Test_00_createRecordBase() >> OK! (7 su 7 sono ok)

    C) @GeneratedValue(strategy=GenerationType.SEQUENCE)

    1) H2 con file data.sql >> NO! (solo 5 su 6 sono ok)
    2) H2 con metodo in java Test_00_createRecordBase() >> OK! (7 su 7 sono ok)
    3) MySQL con file data.sql >> NO! (solo 5 su 6 sono ok)
    4) MySQL con metodo in java Test_00_createRecordBase() >> OK! (7 su 7 sono ok)

    D) @GeneratedValue(strategy=GenerationType.TABLE)

    1) H2 con file data.sql >> NO! (solo 5 su 6 sono ok)
    2) H2 con metodo in java Test_00_createRecordBase() >> OK! (7 su 7 sono ok)
    3) MySQL con file data.sql >> NO! (solo 5 su 6 sono ok)
    4) MySQL con metodo in java Test_00_createRecordBase() >> NO! (solo 4 su 7 sono ok)

    Non so come sia possibile che all'autore funzioni tutto con .AUTO. A me funziona tutto sono con .IDENTITY. Quello che vorrei capire sono le motivazioni del fallimento di alcune di queste strategie. Sapresti rispondere a questa domanda?

    SpringbootJPADemoApplicationTests.java
    
    package com.apress.demo;
    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertNotNull;
    import static org.junit.Assert.assertTrue;
    import java.util.List;
    import org.junit.FixMethodOrder;
    import org.junit.Ignore;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.junit.runners.MethodSorters;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.domain.Sort;
    import org.springframework.data.domain.Sort.Direction;
    import org.springframework.data.domain.Sort.Order;
    import org.springframework.test.context.junit4.SpringRunner;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest(classes=SpringbootJPADemoApplication.class)
    @FixMethodOrder(MethodSorters.NAME_ASCENDING)
    public class SpringbootJPADemoApplicationTests
    {
    
    	@Autowired
    	private UserRepository userRepository;
    
    	// Sostituisce il file data.sql
    	/*@Test
    	public void Test_00_createRecordBase() {
    		User user = new User(null, "John", "john@gmail.com",true);
    		User savedUser = userRepository.save(user);
    		user = new User(null, "Rod", "rod@gmail.com");
    		savedUser = userRepository.save(user);
    		user = new User(null, "Becky", "becky@gmail.com");
    		savedUser = userRepository.save(user);
    		List<User> users = userRepository.findAll();
    		// Si verifica che 'users' non sia nullo.
    		assertNotNull(users);
    		// Si verifica che 'users' non sia vuoto.
    		assertTrue(!users.isEmpty());
    	}*/
    
    	@Test
    	public void Test_01_findAllUsers()  {
    		List<User> users = userRepository.findAll();
    		assertNotNull(users);
    		assertTrue(!users.isEmpty());
    		
    	}
    
    	@Test
    	public void Test_02_findUserById()  {
    		User user = userRepository.getOne(1);
    		assertNotNull(user);
    	}
    
    	@Test //@Ignore
    	public void Test_03_createUser() {
    		User user = new User(null, "Paul", "paul@gmail.com");
    		User savedUser = userRepository.save(user);
    		User newUser = userRepository.findById(savedUser.getId()).get();
    		assertEquals("Paul", newUser.getName());
    		assertEquals("paul@gmail.com", newUser.getEmail());
    	}
    
    	@Test
    	public void Test_04_getUsersSortByName() {
    		Sort sort = new Sort(Direction.ASC, "name");
    		List<User> users = userRepository.findAll(sort);
    		assertNotNull(users);
    	}
    
    	@Test
    	public void Test_05_getUsersSortByNameAscAndIdDesc() {
    		Order order1 = new Order(Direction.ASC, "name");
    		Order order2 = new Order(Direction.DESC, "id");
    		Sort sort = Sort.by(order1, order2);
    		List<User> users = userRepository.findAll(sort);
    		assertNotNull(users);
    	}
    
    	@Test
    	public void Test_06_getUsersByPage() {
    		Sort sort = new Sort(Direction.ASC, "name");
    		int size = 25;
    		int page = 0; //zero-based page index.
    		Pageable pageable = PageRequest.of(page, size, sort);
    		Page<User> usersPage = userRepository.findAll(pageable);
    		System.out.println(
    				"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Stampa in console %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"
    		);
    		System.out.println(usersPage.getTotalElements()); //Returns the total amount of elements.
    		System.out.println(usersPage.getTotalPages());//Returns the number of total pages.
    		System.out.println(usersPage.hasNext());
    		System.out.println(usersPage.hasPrevious());
    		List<User> usersList = usersPage.getContent();
    		System.out.println(
    				"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Fine %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"
    		);
    		assertNotNull(usersList);
    	}
    }
    
    
    data.sql
    
    insert into users(id, name, email,disabled) values(1,'John','john@gmail.com', false);
    insert into users(id, name, email,disabled) values(2,'Rod','rod@gmail.com', false);
    insert into users(id, name, email,disabled) values(3,'Becky','becky@gmail.com', true);
    
    In precedenza hai scritto che se si sceglie AUTO l'ORM (Hibernate) sceglie la strategia di generazione del ID che risulta meglio per il dialect selezionato. Stando a questo livello generico, non si può controllare come viene definita la colonna id. Questo è il punto che proprio non comprendo. Anche se non si riesce a controllare come viene definita la colonna id l'ORM dovrebbe sapere quale approccio ha usato e usare sempre quello, sia per caricare data.sql, sia per caricare il nuovo record relativo a "Paul"!

    User.java:
    
    package com.apress.demo;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    @Entity
    @Table(name="USERS")
    public class User
    {
    
    	@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    	private Integer id;
    	@Column(nullable=false, length=191)
    	private String name;
    	@Column(nullable=false, unique=true, length=191)
    	private String email;
    	private boolean disabled;
    
    	public User()
    	{
    	}
    
    	public User(Integer id, String name, String email)
    	{
    		this.id = id;
    		this.name = name;
    		this.email = email;
    	}
    	public User(Integer id, String name, String email, boolean disabled)
    	{
    		this.id = id;
    		this.name = name;
    		this.email = email;
    		this.disabled = disabled;
    	}
    	public Integer getId()
    	{
    		return id;
    	}
    
    	public void setId(Integer id)
    	{
    		this.id = id;
    	}
    
    	public String getName()
    	{
    		return name;
    	}
    
    	public void setName(String name)
    	{
    		this.name = name;
    	}
    
    	public String getEmail()
    	{
    		return email;
    	}
    
    	public void setEmail(String email)
    	{
    		this.email = email;
    	}
    
    	public boolean isDisabled()
    	{
    		return disabled;
    	}
    
    	public void setDisabled(boolean disabled)
    	{
    		this.disabled = disabled;
    	}
    	
    }
    
    Grazie anticipatamente
  • Re: JPA funziona con H2 ma non con MySQL

    giannino1995 ha scritto:


    Cambiando il tipo di assegnazione dell'id (IDENTITY, SEQUENCE, AUTO, TABLE), il metodo di caricamento (Test_00_createRecordBase() oppure data.sql) e la tipologia di database (H2 o MySQL) sono usciti fuori 16 casi.
    Non so se/quanto abbia senso fare questo tipo di "tentativi". Probabilmente molto poco, perché temo che questo ti abbia solo confuso ancora di più le idee.
    Te l'ho sicuramente già detto in altre discussioni. Tutti quei test SULLA base dati nei vari progetti del libro sono abbastanza critici, perché innanzitutto sono interdipendenti tra di loro. L'autore si è sicuramente limitato a fare il "minimo sindacale" per farli passare, senza poter/dover introdurre ulteriori concetti o mettere troppa "carne al fuoco".

    Il punto è che la base dati generalmente NON si testa! Si arriva normalmente fino a testare i Service mettendo DAO/Repository come dei "mock", che si possono cioè "instrumentare" per farli comportare come si vuole.

    Se proprio si vuole testare andando SUL DB, è una possibilità, una scelta, e in certi progetti aziendali si fa anche. Ma in questo caso allora:
    a) si parla concettualmente di integration-test (non di unit-test) perché si testa anche la integrazione con un DB
    b) ci deve essere un database apposito SOLO per il testing, su cui nessun altro ci "lavora" (e certamente non quello di produzione!!)
    c) prima di ogni singolo test si deve creare uno stato "stabile" sul DB. Questo non è facile in generale ed esistono anche librerie/strumenti appositi. Se ben ricordo, esiste una libreria che si chiama DbUnit (credo sia ancora in sviluppo attivo) che aiuta appunto a creare uno stato stabile prima dei test.

    E tutto questo va ben OLTRE le questioni spiegate nel libro! Quindi quei test vanno presi per quello che sono .... proprio solo come dimostrazione/spiegazione "guarda, è così, punto".

    giannino1995 ha scritto:


    Non so come sia possibile che all'autore funzioni tutto con .AUTO. A me funziona tutto sono con .IDENTITY. Quello che vorrei capire sono le motivazioni del fallimento di alcune di queste strategie. Sapresti rispondere a questa domanda?
    Ho ripreso il springboot-jpa-demo originale (ho lo zip di quell'intero repo) e gli ho fatto SOLO le seguenti modifiche (niente IDE).

    Nel pom.xml
    - <mysql.version>8.0.21</mysql.version> in <properties>
    Nel application-prod.properties:
    - driver: com.mysql.cj.jdbc.Driver
    - nel url: ?serverTimezone=UTC&createDatabaseIfNotExist=true
    - mia password root
    - in più: spring.datasource.initialization-mode=always (perché MySQL NON essendo embedded altrimenti non userebbe lo schema.sql)

    Non ho toccato/rimosso altro!! (e c'è GenerationType.AUTO)


    TEST (H2):
    mvn test
    Tests run: 6, Failures: 0, Errors: 0, Skipped: 1

    TEST (MySQL, nota: ho droppato tutti i db dai vari progetti provati):
    mvn test -Dspring.profiles.active=prod
    Tests run: 6, Failures: 0, Errors: 0, Skipped: 1

    Stop, punto.
Devi accedere o registrarti per scrivere nel forum
20 risposte