{"id":20533849,"url":"https://github.com/anicolaspp/dojai","last_synced_at":"2025-07-15T13:03:29.578Z","repository":{"id":44517140,"uuid":"186288190","full_name":"anicolaspp/dojai","owner":"anicolaspp","description":"A JDBC Driver for OJAI","archived":false,"fork":false,"pushed_at":"2022-03-22T14:55:58.000Z","size":167,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-01T13:34:10.247Z","etag":null,"topics":["bigdata","cluster","hibernate","java","jdbc","jdbc-driver","mapr","mapr-database","mapr-db","ojai","ojai-testing","scale","sql","testing"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anicolaspp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-12T18:10:05.000Z","updated_at":"2022-03-22T14:55:50.000Z","dependencies_parsed_at":"2022-09-03T23:21:57.982Z","dependency_job_id":null,"html_url":"https://github.com/anicolaspp/dojai","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/anicolaspp/dojai","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anicolaspp%2Fdojai","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anicolaspp%2Fdojai/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anicolaspp%2Fdojai/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anicolaspp%2Fdojai/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anicolaspp","download_url":"https://codeload.github.com/anicolaspp/dojai/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anicolaspp%2Fdojai/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265437598,"owners_count":23765124,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bigdata","cluster","hibernate","java","jdbc","jdbc-driver","mapr","mapr-database","mapr-db","ojai","ojai-testing","scale","sql","testing"],"created_at":"2024-11-16T00:24:04.136Z","updated_at":"2025-07-15T13:03:29.505Z","avatar_url":"https://github.com/anicolaspp.png","language":"Java","readme":"# dojai (pronounced Du-jai)\n\nA JDBC Driver for OJAI\n\nThis 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. \n\nWe 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. \n\n- [Plain SQL](https://github.com/anicolaspp/dojai/blob/master/README.md#plain-sql)\n- [Working with Hibernate](https://github.com/anicolaspp/dojai/blob/master/README.md#working-with-hibernate)\n    - [Hibernate Configuration](https://github.com/anicolaspp/dojai/blob/master/README.md#hibernate-configuration)\n    - [Entity Definition](https://github.com/anicolaspp/dojai/blob/master/README.md#our-hibernate-employee-entity)\n    - [Running Hibernate Queries](https://github.com/anicolaspp/dojai/blob/master/README.md#running-hibernate-queries)\n        - [Adding an Employee](https://github.com/anicolaspp/dojai/blob/master/README.md#adding-an-employee)\n        - [Loading all Employees](https://github.com/anicolaspp/dojai/blob/master/README.md#loading-all-employees)\n- [Limitation](https://github.com/anicolaspp/dojai/blob/master/README.md#limitations)\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.github.anicolaspp\u003c/groupId\u003e\n  \u003cartifactId\u003edojai\u003c/artifactId\u003e\n  \u003cversion\u003e1.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Plain SQL\n\nThe following get all records from a MapR Database table given some condition. \n\n```java\nimport lombok.val;\n\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\n\npublic class App {\n    public static void main(String[] args) throws SQLException, ClassNotFoundException {\n    \n        Class.forName(\"anicolaspp.sql.DojaiDriver\");\n        \n        val connection = DriverManager.getConnection(\"dojai:mapr:\");\n    \n        System.out.println(connection.getClass().toString());\n        \n        val statement = connection.createStatement();\n        \n        String sql = \"select _id, name from user.mapr.some_data where name = pepe or name = lolo limit 10\";\n        \n        val result = statement.executeQuery(sql);\n        \n        while (result.next()) {\n            System.out.println(result.getString(0));\n            System.out.println(result.getString(1));\n        }\n    \n        System.out.println(statement);\n    }\n}\n```\n\nWe can also do `INSERT INTO` that works as you might expect. \n\n```java\nimport lombok.val;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\n\nprivate static void testInsert(Connection connection) throws SQLException {\n\n    String sql = \"INSERT INTO `/user/mapr/tables/t2` (_id, name, age) select name as n from `/user/mapr/tables/t1`\";\n\n    val statement = connection.createStatement();\n\n    statement.executeUpdate(sql);\n}\n```\n\nNotice 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`.\n\nNotice that we are selecting data from `/user/mapr/tables/t1` and writing to `/user/mapr/tables/t2`.\n\nWe can also insert static values in the following way.\n\n```java\nimport lombok.val;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\n\nprivate static void testInsertValues(Connection connection) throws SQLException {\n\n    String sql = \"INSERT INTO `/user/mapr/tables/users` (_id, name, age) Values (\"001\", \"nick\", 30)\";\n\n    val statement = connection.createStatement();\n\n    statement.executeUpdate(sql);\n}\n```\n\nOf course we could use this to insert data in a dynamic way. \n\n```java\nimport lombok.val;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\n\nprivate static void testInsertFromStream(Connection connection, Stream\u003cUser\u003e users) throws SQLException {\n\n    String sql = \"INSERT INTO `/user/mapr/tables/users` (_id, name, age) Values (%s, %s, %d)\";\n\n    users\n        .map(user -\u003e String.format(sql, user.getId(), user.getName(), user.getAge())\n        .map(sqlToRun -\u003e connection.createStatement().executeUpdate(sqlToRun))\n        .forEach(System.out::println)\n}\n```\n\nDeletes can be executed in the following way.\n\n```java\n private static void testDeleteAll(Connection connection) throws SQLException {\n        String sql = \"DELETE FROM `/user/mapr/tables/dojai`\";\n\n        val statement = connection.createStatement();\n\n        statement.executeUpdate(sql);\n    }\n\n    private static void testDeleteSome(Connection connection) throws SQLException {\n        String sql = \"DELETE FROM `/user/mapr/tables/t1` WHERE age = 40\";\n\n        val statement = connection.createStatement();\n\n        statement.executeUpdate(sql);\n    }\n```\n\n# Working with Hibernate\n\nSince `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. \n\n## Hibernate Configuration\n\nFirst, let's look at how we can configure Hibernate so it uses `DOJAI` as a datasource.\n\nThe following snippet shows how we can optain a Hibernate `SessionFactory`. \n\n```java\n private static SessionFactory buildSessionFactory() {\n        try {\n            val configuration = new Configuration();\n            configuration.setProperty(\"hibernate.connection.url\", \"dojai:mapr:mem:\");\n\n            Class.forName(\"com.github.anicolaspp.sql.DojaiDriver\");\n\n            DriverManager.registerDriver(InMemoryDriver.apply());\n\n            configuration.setProperty(\"hibernate.dialect\", \"org.hibernate.dialect.MySQL5Dialect\");\n            configuration.setProperty(\"hibernate.connection.driver_class\", \"com.github.anicolaspp.sql.DojaiDriver\");\n            configuration.setProperty(\"hibernate.show_sql\", \"true\");\n\n            configuration.setProperty(\"hibernate.c3p0.min_size\", \"5\");\n            configuration.setProperty(\"hibernate.c3p0.max_size\", \"20\");\n            configuration.setProperty(\"hibernate.c3p0.timeout\", \"300\");\n            configuration.setProperty(\"hibernate.c3p0.max_statements\", \"50\");\n            configuration.setProperty(\"hibernate.c3p0.idle_test_period\", \"3000\");\n\n            configuration.addPackage(\"com.github.anicolaspp.hibernate\");\n            configuration.addAnnotatedClass(Employee.class);\n\n            return configuration.buildSessionFactory();\n        } catch (Throwable ex) {\n            System.err.println(\"Failed to create sessionFactory object.\" + ex);\n            throw new ExceptionInInitializerError(ex);\n        }\n    }\n```\n\nLet's review some interesting features of the code above.\n\n- `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](https://github.com/anicolaspp/ojai-testing) or `dojai:mapr:` for a real implementation of `MapR Database`. \n\n- `configuration.setProperty(\"hibernate.dialect\", \"org.hibernate.dialect.MySQL5Dialect\");` Notice we use `MySQL5Dialect`. \n\n- `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](https://github.com/anicolaspp/ojai-testing) or real `MapR Database` based on the `\"hibernate.connection.url\"` described above. \n\n## Our Hibernate Employee Entity\n\n```java\n@Entity\n@Table(name = \"`anicolaspp/user/mapr/tables/employee`\")\n@ToString\npublic class Employee {\n\n    @Id\n    @GenericGenerator(name=\"system-uuid\", strategy = \"uuid\")\n    @Column(name = \"_id\")\n    private String id;\n\n    @Column(name = \"first_name\")\n    private String firstName;\n\n    @Column(name = \"last_name\")\n    private String lastName;\n\n    @Column(name = \"salary\")\n    private int salary;\n\n    public Employee() {\n    }\n\n    @PrePersist\n    private void generateCodeIdentifier(){\n        id = \"\\\"\" + UUID.randomUUID().toString() + \"\\\"\";\n    }\n\n    public Employee(String firstName, String lastName, int salary) {\n        this.firstName = firstName;\n        this.lastName = lastName;\n        this.salary = salary;\n    }\n\n    public String getId() {\n        return \"\\\"\" + id + \"\\\"\";\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public void setFirstName(String first_name) {\n        this.firstName = first_name;\n    }\n\n    public String getLastName() {\n        return lastName;\n    }\n\n    public void setLastName(String last_name) {\n        this.lastName = last_name;\n    }\n\n    public int getSalary() {\n        return salary;\n    }\n\n    public void setSalary(int salary) {\n        this.salary = salary;\n    }\n}\n```\n\nA few things to notice:\n\n```java\n@Table(name = \"`anicolaspp/user/mapr/tables/employee`\") \n``` \nShows the table to be used. In case of using [OJAI Testing Project](https://github.com/anicolaspp/ojai-testing), 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. \n\n```java\n    @Id\n    @GenericGenerator(name=\"system-uuid\", strategy = \"uuid\")\n    @Column(name = \"_id\")\n    private String id;\n```\nNotice 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. \n\n## Running Hibernate Queries\n\n### Adding an Employee\n\n```java\n public String addEmployee(String fname, String lname, int salary){\n        val session = factory.openSession();\n        Transaction tx = null;\n        String employeeID = \"null\";\n\n        try {\n            tx = session.beginTransaction();\n            val employee = new Employee(fname, lname, salary);\n            System.out.println(session.save(employee));\n\n            tx.commit();\n        } catch (HibernateException e) {\n            if (tx!=null) tx.rollback();\n            e.printStackTrace();\n        } finally {\n            session.close();\n        }\n        return employeeID;\n    }\n\n```\n\n### Loading all employees\n\n```java\n public void listEmployees( ){\n        val session = factory.openSession();\n        Transaction tx = null;\n\n        try {\n            tx = session.beginTransaction();\n            List\u003cEmployee\u003e employees = session.createQuery(\"FROM Employee\").getResultList();\n\n            for (Employee obj : employees) {\n                System.out.print(\"  First Name: \" + employee.getFirstName());\n                System.out.print(\"  Last Name: \" + employee.getLastName());\n                System.out.println(\"  Salary: \" + employee.getSalary());\n            }\n\n            tx.commit();\n        } catch (HibernateException e) {\n            if (tx!=null) tx.rollback();\n            e.printStackTrace();\n        } finally {\n            session.close();\n        }\n    }\n\n```\n\n### Limitations\n\nAt this early stage \n\n- Not Start (`*`) schema allowed. Queries should use `select column1 [,\u003ccolumn2\u003e, ...] ....`.\n- No query should go across tables (`join`, etc...).\n- Only `select`, `insert into` and `delete` queries are in place at this point, we are adding more soon. \n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanicolaspp%2Fdojai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanicolaspp%2Fdojai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanicolaspp%2Fdojai/lists"}