dojai

A JDBC Driver for OJAI

License

License

GroupId

GroupId

com.github.anicolaspp
ArtifactId

ArtifactId

dojai
Last Version

Last Version

1.0.0
Release Date

Release Date

Type

Type

jar
Description

Description

dojai
A JDBC Driver for OJAI
Project URL

Project URL

https://github.com/anicolaspp/dojai
Project Organization

Project Organization

simpligility technologies inc.
Source Code Management

Source Code Management

https://github.com/anicolaspp/dojai

Download dojai

How to add to project

<!-- https://jarcasting.com/artifacts/com.github.anicolaspp/dojai/ -->
<dependency>
    <groupId>com.github.anicolaspp</groupId>
    <artifactId>dojai</artifactId>
    <version>1.0.0</version>
</dependency>
// https://jarcasting.com/artifacts/com.github.anicolaspp/dojai/
implementation 'com.github.anicolaspp:dojai:1.0.0'
// https://jarcasting.com/artifacts/com.github.anicolaspp/dojai/
implementation ("com.github.anicolaspp:dojai:1.0.0")
'com.github.anicolaspp:dojai:jar:1.0.0'
<dependency org="com.github.anicolaspp" name="dojai" rev="1.0.0">
  <artifact name="dojai" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.github.anicolaspp', module='dojai', version='1.0.0')
)
libraryDependencies += "com.github.anicolaspp" % "dojai" % "1.0.0"
[com.github.anicolaspp/dojai "1.0.0"]

Dependencies

compile (7)

Group / Artifact Type Version
com.github.jsqlparser : jsqlparser jar 1.4
com.mapr.db » maprdb jar 6.1.0-mapr
com.mapr.db » maprdb-java jar 6.1.0-mapr
com.mapr.ojai » mapr-ojai-driver jar 6.1.0-mapr
org.ojai : ojai jar 3.0-mapr-1808
com.github.anicolaspp : ojai-testing_2.12 jar 1.0.7
com.github.anicolaspp : ojai-scala-generics_2.11 jar 1.0.0

provided (1)

Group / Artifact Type Version
org.projectlombok : lombok jar 1.18.8

test (7)

Group / Artifact Type Version
org.hibernate : hibernate-core jar 5.4.3.Final
javax.xml.bind : jaxb-api jar 2.3.1
com.sun.xml.bind : jaxb-core jar 2.3.0.1
com.sun.xml.bind : jaxb-impl jar 2.3.2
com.sun.activation : javax.activation jar 1.2.0
org.hibernate : hibernate-c3p0 jar 5.4.3.Final
org.junit.jupiter : junit-jupiter jar RELEASE

Project Modules

There are no modules declared in this project.

dojai (pronounced Du-jai)

A JDBC Driver for OJAI

This is an experimental library that allows connecting to MapR Database through JDBC. MapR Database is a NoSQL Database, so not everything that works on regular SQL will work here.

We should be able to run queries that do not involve more than one table. In other words, not joins and not aggregations. Those are normally run using analytical tools such as Apache Drill.

<dependency>
  <groupId>com.github.anicolaspp</groupId>
  <artifactId>dojai</artifactId>
  <version>1.0.0</version>
</dependency>

Plain SQL

The following get all records from a MapR Database table given some condition.

import lombok.val;

import java.sql.DriverManager;
import java.sql.SQLException;

public class App {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
    
        Class.forName("anicolaspp.sql.DojaiDriver");
        
        val connection = DriverManager.getConnection("dojai:mapr:");
    
        System.out.println(connection.getClass().toString());
        
        val statement = connection.createStatement();
        
        String sql = "select _id, name from user.mapr.some_data where name = pepe or name = lolo limit 10";
        
        val result = statement.executeQuery(sql);
        
        while (result.next()) {
            System.out.println(result.getString(0));
            System.out.println(result.getString(1));
        }
    
        System.out.println(statement);
    }
}

We can also do INSERT INTO that works as you might expect.

import lombok.val;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

private static void testInsert(Connection connection) throws SQLException {

    String sql = "INSERT INTO `/user/mapr/tables/t2` (_id, name, age) select name as n from `/user/mapr/tables/t1`";

    val statement = connection.createStatement();

    statement.executeUpdate(sql);
}

Notice that in the previous INSERT example, _id is autogenerated when inserting into t2 since it was not selected from t1, name is filled up with the select part of the query, and age is inserted as null.

Notice that we are selecting data from /user/mapr/tables/t1 and writing to /user/mapr/tables/t2.

We can also insert static values in the following way.

import lombok.val;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

private static void testInsertValues(Connection connection) throws SQLException {

    String sql = "INSERT INTO `/user/mapr/tables/users` (_id, name, age) Values ("001", "nick", 30)";

    val statement = connection.createStatement();

    statement.executeUpdate(sql);
}

Of course we could use this to insert data in a dynamic way.

import lombok.val;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

private static void testInsertFromStream(Connection connection, Stream<User> users) throws SQLException {

    String sql = "INSERT INTO `/user/mapr/tables/users` (_id, name, age) Values (%s, %s, %d)";

    users
        .map(user -> String.format(sql, user.getId(), user.getName(), user.getAge())
        .map(sqlToRun -> connection.createStatement().executeUpdate(sqlToRun))
        .forEach(System.out::println)
}

Deletes can be executed in the following way.

 private static void testDeleteAll(Connection connection) throws SQLException {
        String sql = "DELETE FROM `/user/mapr/tables/dojai`";

        val statement = connection.createStatement();

        statement.executeUpdate(sql);
    }

    private static void testDeleteSome(Connection connection) throws SQLException {
        String sql = "DELETE FROM `/user/mapr/tables/t1` WHERE age = 40";

        val statement = connection.createStatement();

        statement.executeUpdate(sql);
    }

Working with Hibernate

Since DOJAI is implemented in terms of JDBC, we can integrate it with Hibernate so we dont have to create the SQL queries manually, instead, we can relie of Hibernate to do this work while we focus on the application logic.

Hibernate Configuration

First, let's look at how we can configure Hibernate so it uses DOJAI as a datasource.

The following snippet shows how we can optain a Hibernate SessionFactory.

 private static SessionFactory buildSessionFactory() {
        try {
            val configuration = new Configuration();
            configuration.setProperty("hibernate.connection.url", "dojai:mapr:mem:");

            Class.forName("com.github.anicolaspp.sql.DojaiDriver");

            DriverManager.registerDriver(InMemoryDriver.apply());

            configuration.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
            configuration.setProperty("hibernate.connection.driver_class", "com.github.anicolaspp.sql.DojaiDriver");
            configuration.setProperty("hibernate.show_sql", "true");

            configuration.setProperty("hibernate.c3p0.min_size", "5");
            configuration.setProperty("hibernate.c3p0.max_size", "20");
            configuration.setProperty("hibernate.c3p0.timeout", "300");
            configuration.setProperty("hibernate.c3p0.max_statements", "50");
            configuration.setProperty("hibernate.c3p0.idle_test_period", "3000");

            configuration.addPackage("com.github.anicolaspp.hibernate");
            configuration.addAnnotatedClass(Employee.class);

            return configuration.buildSessionFactory();
        } catch (Throwable ex) {
            System.err.println("Failed to create sessionFactory object." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

Let's review some interesting features of the code above.

  • configuration.setProperty("hibernate.connection.url", "dojai:mapr:mem:"); allows us the define what kind of OJAI connection we want. We can choose between dojai:mapr:mem: for an in-memery implementation of MapR Database using the OJAI Testing Project or dojai:mapr: for a real implementation of MapR Database.

  • configuration.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect"); Notice we use MySQL5Dialect.

  • configuration.setProperty("hibernate.connection.driver_class", "com.github.anicolaspp.sql.DojaiDriver"); indicates that Hibernate will use the DojaiDriver. Internally, the DojaiDriver will select to use OJAI Testing Project or real MapR Database based on the "hibernate.connection.url" described above.

Our Hibernate Employee Entity

@Entity
@Table(name = "`anicolaspp/user/mapr/tables/employee`")
@ToString
public class Employee {

    @Id
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    @Column(name = "_id")
    private String id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @Column(name = "salary")
    private int salary;

    public Employee() {
    }

    @PrePersist
    private void generateCodeIdentifier(){
        id = "\"" + UUID.randomUUID().toString() + "\"";
    }

    public Employee(String firstName, String lastName, int salary) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.salary = salary;
    }

    public String getId() {
        return "\"" + id + "\"";
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String first_name) {
        this.firstName = first_name;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String last_name) {
        this.lastName = last_name;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }
}

A few things to notice:

@Table(name = "`anicolaspp/user/mapr/tables/employee`") 

Shows the table to be used. In case of using OJAI Testing Project, the table path must start with anicolaspp. In case of using a real MapR Database cluster, this path should be a real path in the cluster.

    @Id
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    @Column(name = "_id")
    private String id;

Notice that id column in this case is being mapped to _id, internally the identity column for MapR Database. Also it is important to mention that MapR Database does not auto generate ids, they must be managed on the client side. In our case we use the generateCodeIdentifier function for that. Hibernate calls this function before saving new entities.

Running Hibernate Queries

Adding an Employee

 public String addEmployee(String fname, String lname, int salary){
        val session = factory.openSession();
        Transaction tx = null;
        String employeeID = "null";

        try {
            tx = session.beginTransaction();
            val employee = new Employee(fname, lname, salary);
            System.out.println(session.save(employee));

            tx.commit();
        } catch (HibernateException e) {
            if (tx!=null) tx.rollback();
            e.printStackTrace();
        } finally {
            session.close();
        }
        return employeeID;
    }

Loading all employees

 public void listEmployees( ){
        val session = factory.openSession();
        Transaction tx = null;

        try {
            tx = session.beginTransaction();
            List<Employee> employees = session.createQuery("FROM Employee").getResultList();

            for (Employee obj : employees) {
                System.out.print("  First Name: " + employee.getFirstName());
                System.out.print("  Last Name: " + employee.getLastName());
                System.out.println("  Salary: " + employee.getSalary());
            }

            tx.commit();
        } catch (HibernateException e) {
            if (tx!=null) tx.rollback();
            e.printStackTrace();
        } finally {
            session.close();
        }
    }

Limitations

At this early stage

  • Not Start (*) schema allowed. Queries should use select column1 [,<column2>, ...] .....
  • No query should go across tables (join, etc...).
  • Only select, insert into and delete queries are in place at this point, we are adding more soon.

Versions

Version
1.0.0