J-Framework Data

Data API makes the development of database-driven applications in Java more convenient and efficient.

Dependency

Most likely, you will NOT need to add the direct dependency of this project, since it’s already included in the parent projects or the other framework projects. However, if you need to, the dependency is as follows.

<!-- https://mvnrepository.com/artifact/com.jalalkiswani/j-framework-core -->
<dependency>
    <groupId>com.jalalkiswani</groupId>
    <artifactId>j-framework-data</artifactId>
    <version>7.0.0-SNAPSHOT</version>
</dependency>

Main Features

  • Elegant and easy-to-use API (proven to work with minimum Java experience)

  • Enables the best of the following in the same App:

    • JDBC (SQL inside Java),

    • JPA ((Java classes with mapping to database without SQL code)

    • NoSQL (JSON and document-based database such as MongoDB)

  • Unified configuration file for JDBC, JPA, and NoSQL, no need for pirsistence.xml or any other files.

  • Built-in connection pooling with intensive optimization configurations.

  • Multi-database support

  • Intensive Logging,

  • Built in Audit-Trail.

  • Built in caching mechanism.

  • Efficient Timing for queries with near-zero overhead.

  • Provide connections with the needed client information for debugging and monitoring purposes.

  • Dynamic Data Access API.

  • Auto database synchronization based on JPA entities (thanks to Hibernate)

  • Enable having SQL queries inside SQL files, which is more convenient and easily maintainable by database developers.

  • Sophisticated support for stored procedures and custom database objects

Basic Usage

JDBC

For JDBC data access, create JKDataAccessService as follows, then enjoy calling SQL statements on database with which could be the most convenient JDBC approach for people coming from Oracle and Database programming background.

 JKDataAccessService jdbc = JKDataAccessFactory.getDataAccessService();

JPA

For JPA and ORM Mapping, place your JPA entities in the com.app.entities package, then create JKObjectDataAccess as follows, that’s it, now you have a wonderful set of methods to be used for your database-driven applications.

JKObjectDataAccess orm = JKDataAccessFactory.getObjectDataAccessService();

You can always change the default package names of the entities using the db-entities-packages property in the config file src/main/resources/config.properties.

Even if you would like to use JDBC in your code, I always prefer to build JPA entities to create the database structure on the dev environment, then create a base script that I use to migrate to other environments, for me, it’s convenient, easy, and Java :)

Configurations

The framework creates a local file-based H2 database by default, so basically, no configuration is needed.

This could be a good idea for testing automation, proof-of-concepts, or trying things out. However, for real-life applications, you will need to go with production-grade database engines.

To manage the configuration of your database(s), please refer to the Config Guide.

Naming Strategy

By default, the framework will map to database tables according to the following rules:

  • Convert the camel-case names to snake-case (underscores).

  • Lowercase the names.

In the case of Oracle:

  1. All the names will be converted to uppercase

  2. Any field with a reserved keyword name will be post-fixed with an underscore, for example: a field with a UID name will be mapped to a column with the name UID_

Default Configuration

Below are the default configurations, which are set in the default.config.properties in the framework

#H2 Configurations
#hibernate.connection.driver_class = org.h2.Driver
#hibernate.connection.url = jdbc:h2:file:~/.jk/h2-db/h2db2.data
#hibernate.dialect = org.hibernate.dialect.H2Dialect

#MySQl Config
#hibernate.connection.driver_class = com.mysql.jdbc.Driver
#hibernate.connection.url = jdbc:mysql://localhost:3306/app?createDatabaseIfNotExist=true
#hibernate.dialect = org.hibernate.dialect.MySQL57Dialect
#hibernate.connection.username = root
#hibernate.connection.password = 123456

#Oracle Config, you will need to add oracle driver dependency,
#checkout, option 4(System Path): https://www.mkyong.com/maven/how-to-add-oracle-jdbc-driver-in-your-maven-local-repository/

#hibernate.connection.driver_class = oracle.jdbc.driver.OracleDriver
#hibernate.connection.url = jdbc:oracle:thin:@localhost:1521/orclpdb1
#hibernate.dialect = org.hibernate.dialect.Oracle10gDialect
#hibernate.c3p0.preferredTestQuery=SELECT 1 FROM DUAL

#hibernate.connection.username = sa
#hibernate.connection.password = sa

#To build database structure from Entities
#hibernate.hbm2ddl.auto=update
#Package to scan for JPA entities, CSV value is allowed for multiple packages
#db-entities-packages=com.app,

nosql.url=mongodb://localhost:27017/
nosql.db.name=app
nosql.db.user=
nosql.db.password=

hibernate.connection.url = jdbc:h2:file:~/.jk/h2-db/h2db2.data;IGNORECASE=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;DATABASE_TO_UPPER=FALSE;
hibernate.connection.username = sa
hibernate.connection.password = sa

#hibernate.c3p0.initialPoolSize=1
hibernate.c3p0.min_size=1
hibernate.c3p0.max_size=100

#important to to avoid AUTO id error in JPA: https://stackoverflow.com/questions/49813666/table-dbname-hibernate-sequence-doesnt-exist
hibernate.id.new_generator_mappings=false
#hibernate.physical_naming_strategy: already calculated internally
#this will ensure escaping all fields that are keywords
hibernate.globally_quoted_identifiers=true
#hibernate.use_sql_comments=false
hibernate.format_sql=true
hibernate.show_sql=false
hibernate.hbm2ddl.auto=update

#hot deployment
hibernate.c3p0.privilegeSpawnedThreads=true
hibernate.c3p0.contextClassLoaderSource=library

#connections cleanup
#seconds, connections stay un-used for n seconds 
hibernate.c3p0.timeout=3600
hibernate.c3p0.unreturnedConnectionTimeout=3600
#millis, this time could be needed in the init phase of the pool, need verification
hibernate.c3p0.checkoutTimeout=20000
hibernate.c3p0.testConnectionOnCheckout=true
#refresh the connection anyways after this timeout
#hibernate.c3p0.maxConnectionAge=18000
hibernate.c3p0.debugUnreturnedConnectionStackTraces=true

#millis-seconds
hibernate.c3p0.acquire_increment=1
hibernate.c3p0.acquireRetryDelay=10000
hibernate.c3p0.acquireRetryAttempts=1

hibernate.c3p0.idleConnectionTestPeriod=30

hibernate.c3p0.maxStatements=1000
hibernate.c3p0.maxStatementsPerConnection=100
hibernate.c3p0.numHelperThreads=10
#hibernate.c3p0.maxAdministrativeTaskTime=100
#hibernate.c3p0.automaticTestTable=
#hibernate,c3p0,plain,dbcp
jk.data.datasource.impl=hibernate
hibernate.connection.autocommit=true

Git DataAccess

JKGitWrapper is a full wrapper that enables full communication with a Git repository.

A full example can be found below.

String url = "Remote Repository URL";
String userName = "UserName";
String password = "Password";
String localPath = "local-source-path";

JKGitWrapper gw = new JKGitWrapper();

gw.url(url).user(userName).password(password).localPath(localPath);

Git git = gw.cloneRepo();

JKIOUtil.createDirectory(gw.getLocalPath(), "dir1");
JKIOUtil.createDirectory(gw.getLocalPath(), "dir2");
JKIOUtil.createDirectory(gw.getLocalPath(), "dir2/dir2-1");
JKIOUtil.createDirectory(gw.getLocalPath(), "dir3");

JKIOUtil.writeDataToFile("Hello from uncle Jalal0", new File(gw.getLocalPath(), "dir1/test.txt"));
JKIOUtil.writeDataToFile("Hello from uncle Jalal1", new File(gw.getLocalPath(), "dir2/test.txt"));
JKIOUtil.writeDataToFile("Hello from uncle Jalal2", new File(gw.getLocalPath(), "dir2/dir2-1/test.txt"));
JKIOUtil.writeDataToFile("Hello from uncle Jalal3", new File(gw.getLocalPath(), "dir3/test.txt"));

gw.add(git);
gw.commit(git);
gw.push(git);

Multi-Database Support

In the config file, you can add a prefix to the same configuration used in normal database setup.

mydb.hibernate.connection.url =jdbc:oracle:thin:@0.0.0.0:1521:DB
mydb.hibernate.connection.username =user
mydb.hibernate.connection.password = pass

Then, in your DataAccess class, create an instance of the JKDataAccessService with the prefix:

JKDataAccessService service=JKDataAccessFactory.getDataAccessService("mydb);

To unify the database prefix in one place, it is recommended to create a DataAccess super class that extends one of the JKDataAccessImpl classes and pass the prefix as a constructor parameter to superclass, in this case, other team members shall only extend your new DataAccess class to connect to the right database.