[JAVA] Essayez HiveRunner

motivation

Je souhaite tester les requêtes Hive.

Avec mysql, vous pouvez tester docker-compose relativement facilement, mais je veux tester les requêtes Hive de la même manière, mais que dois-je faire? (Cela semble un peu pénible de devenir fou avec docker ..)

C'est pourquoi j'ai choisi un plug-in qui avait l'air bien et l'ai essayé.

J'ai choisi la git star parmi les quelques outils officiellement introduits. Il y avait beaucoup de HiveRunner. (La version est 4.1.0)

Préparation

HiveRunner teste essentiellement la requête comme un test de Junit.

Il n'a pas besoin de dépendances externes, cela ressemble à une image de HiveServer sur JVM et de Junit exécutant hive sql sur ce HiveServer.

Cette fois, nous allons créer un projet avec maven. Ce qui suit est le pom lors de l'exécution cette fois. Il n'y a pas de raison particulière, mais j'ai spécifié l'émulateur BEELINE.

<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.21.0</version>
            <configuration>
                <forkMode>always</forkMode>
                <systemProperties>
                    <!-- Defaults to HIVE_CLI, other options include BEELINE and HIVE_CLI_PRE_V200 -->
                    <commandShellEmulator>BEELINE</commandShellEmulator>
                </systemProperties>
            </configuration>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <groupId>com.klarna</groupId>
        <artifactId>hiverunner</artifactId>
        <version>4.1.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Je vais l'essayer pour le moment


@RunWith(StandaloneHiveRunner.class)
public class TestHiveRunner {
    @HiveSQL(files = {})
    private HiveShell shell;

    /*
     *Créer une base de données et une table
     */
    @Before
    public void setupSourceDatabase() {
        shell.execute(
                "CREATE DATABASE source_db; " +
                "CREATE TABLE source_db.test_table (year STRING, value INT);");

        shell.execute(
                "CREATE DATABASE my_schema; " +
                "CREATE EXTERNAL TABLE my_schema.result " +
                "(year STRING, value INT) " +
                "STORED AS PARQUET " +
                "TBLPROPERTIES (\"parquet.compress\"=\"snappy\")");
    }

    @Test
    public void testMaxValueByYear() {
        /*
         *Stocker les données de test dans le tableau à agréger
         */
        shell.insertInto("source_db", "test_table")
                .withColumns("year", "value")
                .addRow("2014", 3)
                .addRow("2014", 4)
                .addRow("2015", 2)
                .addRow("2015", 5)
                .commit();

        /*
         *Exécuter une requête agrégée(INSÉRER la requête)
         */
        shell.executeStatement("INSERT INTO my_schema.result " +
                "SELECT " +
                "   year, " +
                "   MAX(value) " +
                "FROM " +
                "   source_db.test_table " +
                "GROUP BY year");

        /*
         *Récupère le résultat de la table où le résultat agrégé est INSERT
         */
        List<Object[]> result = shell.executeStatement("SELECT * FROM my_schema.result");

        assertEquals(2, result.size());
        assertArrayEquals(new Object[]{"2014",4}, result.get(0));
        assertArrayEquals(new Object[]{"2015",5}, result.get(1));
    }
}

Il s'agit d'une version légèrement réécrite du code dans la plupart des exemples ci-dessus.

Il semble que ce soit très facile à tester.

Il est également possible de lire la requête extraite, ou lorsque le nombre d'insertions de données de test augmente et que les données de test sont découpées séparément en tsv, etc.

Jetons un coup d'œil à chacun

@HiveSQL


//Src par défaut/test/Vous faites référence au répertoire des ressources.
@HiveSQL(files = {"create_test_table.sql", "create_max.sql"})
private HiveShell shell;

En spécifiant un fichier SQL dans files = {}, il sera exécuté automatiquement après la création de l'instance.

En définissant @HiveSQL (files = {...}, autoStart = false), il peut être démarré après ** une configuration arbitraire **. (Appelez la méthode start ())

À propos, les éléments qui peuvent être définis dans ** Configuration arbitraire ** sont les suivants. (Il existe de nombreuses méthodes surchargées.)


//Définir HiveConf
void setProperty(String key, String value);
void setHiveConfValue(String key, String value);

//Copier les données de test vers HDFS
void addResource(String targetFile, File sourceFile);

//Enregistrement du script à exécuter au démarrage de HiveShell
// @La même chose est vraie pour HiveSetupScript, mais ce qui suit est un ordre garanti d'exécution du script.
void addSetupScript(String script);
void addSetupScripts(Charset charset, File... scripts);

//Il semble qu'il ouvrira le flux et écrira les données de test sur HDFS
OutputStream getResourceOutputStream(String targetFile);

execute

//Écriture directe
shell.execute("CREATE DATABASE source_db; " +
        "CREATE TABLE source_db.test_table (year STRING, value INT);");


//Il est également possible de lire le SQL découpé
shell.execute(Paths.get("src/test/resources/calculate_max.sql"));

Exécutez un script (requête) sans valeur de retour.

Plusieurs requêtes peuvent être exécutées en les séparant par ;.

Cela signifie-t-il qu'il n'y a pas de valeur de retour car vous pouvez exécuter plusieurs requêtes?

executeQuery & executeStatement


//Vous pouvez également l'exécuter avec executeQuery ci-dessous.
shell.executeStatement("INSERT INTO my_schema.result " +
        "SELECT " +
        "   year, " +
        "   MAX(value) " +
        "FROM " +
        "   source_db.test_table " +
        "GROUP BY year");

//Seul executeQuery peut lire le SQL découpé
shell.execute(Paths.get("src/test/resources/calculate_max.sql"));

Contrairement à ʻexecute, plusieurs requêtes ne peuvent pas être exécutées à la fois, et une erreur se produira si; ʻest inclus à la fin de la phrase.

Au lieu de cela, «List » renverra les résultats de la requête.

insertInto


shell.insertInto("source_db", "test_table")
        .withColumns("year", "value")
        .addRow("2014", 3)
        .addRow("2014", 4)
        .addRow("2015", 2)
        .addRow("2015", 5)
        .commit();

//Vous pouvez également insérer des données à partir de tsv, etc.
shell.insertInto("source_db", "test_table")
        .withColumns("year", "value")
        .addRowsFromTsv(new File("src/test/resources/insert_data_of_test_table.tsv"))
        .commit();

Cette méthode ne sera pas exécutée sans commit ().

La préparation des données de test est gênante, mais il est très pratique de les insérer avec tsv.

Résumé

Cela semble utile (même si cela semble un peu lourd) lorsque vous voulez tester une requête ou essayer une petite requête.

Par contre, dans une table avec un grand nombre de colonnes, un test lorsque la quantité de données de test est grande, un test d'une requête très compliquée, etc., cela semble difficile en termes de mémoire et de vitesse d'exécution, mais que se passe-t-il (README)? Il existe des instructions pour y faire face ..)

Cela dit, il est tentant de pouvoir tester sql pour ruche simplement en mettant hiverunner dans pom.

Recommended Posts

Essayez HiveRunner
Essayez Mockito
Essayez le sélénium
Essayez DbUnit
Essayez Lombok
Essayez d'utiliser libGDX
Essayez d'abord ~ attraper
Essayez d'utiliser Maven
Essayez d'utiliser powermock-mockito2-2.0.2
Essayez d'utiliser GraalVM
Essayez Java 8 Stream
Essayez d'utiliser jmockit 1.48
Essayez d'utiliser SwiftLint
Essayez d'utiliser Log4j 2.0
Essayez Ruby Minitest
Essayez grossièrement Java 9