I have some legacy tables with the same structure in MySQL like:
my_table_01
my_table_02
my_table_03
...
Is there a way I can configure JOOQ codegen to generate only one table/record class which shared by all those tables ?
There are two steps that you have to do in order to achieve what you like to do:
1. Configure the code generator
You'll probably have to exclude my_table_02 and my_table_03 from being generated. You can do this by specifying the <excludes/> tag as documented here.
Optionally, you could use generator strategies (programmatic config) or matcher strategies (XML config) to rename my_table_01 to my_table.
2. Configure your runtime
While running queries against MY_TABLE, you can specify runtime table mapping in order to map MY_TABLE back to my_table_01 or my_table_02 or my_table_03. This mapping works on a per-configuration basis, i.e. it will have the scope of a single query if you're using one configuration per query.
Another option is, of course, to abstract over these suffixes in your client code, e.g. via a table selection method:
public static Table<?> myTable() {
if (something)
return DSL.table("{0}_01", MY_TABLE);
else if (somethingElse)
return DSL.table("{0}_02", MY_TABLE);
...
}
Related
I am new to Java and started with Spring Boot and Spring Data JPA, so I know 2 ways on how to fetch data:
by Repository layer, with Literal method naming: FindOneByCity(String city);
by custom repo, with #Query annotation: #Query('select * from table where city like ?');
Both ways are statical designed.
How should I do to get data of a query that I have to build at run time?
What I am trying to achieve is the possibility to create dynamic reports without touching the code. A table would have records of reports with names and SQl queries with default parameters like begin_date, end_date etc, but with a variety of bodies. Example:
"Sales report by payment method" | select * from sales where met_pay = %pay_method% and date is between %begin_date% and %end_date%;
The Criteria API is mainly designed for that.
It provides an alternative way to define JPA queries.
With it you could build dynamic queries according to data provided at runtime.
To use it, you will need to create a custom repository implementation ant not only an interface.
You will indeed need to inject an EntityManager to create needed objects to create and execute the CriteriaQuery.
You will of course have to write boiler plate code to build the query and execute it.
This section explains how to create a custom repository with Spring Boot.
About your edit :
What I am trying to achieve is the possibility to create dynamic
reports without touching the code. A table would have records of
reports with names and SQl queries with default parameters like
begin_date, end_date etc, but with a variety of bodies.
If the queries are written at the hand in a plain text file, Criteria will not be the best choice as JPQL/SQL query and Criteria query are really not written in the same way.
In the Java code, mapping the JPQL/SQL queries defined in a plain text file to a Map<String, String> structure would be more adapted.
But I have some doubts on the feasibility of what you want to do.
Queries may have specific parameters, for some cases, you would not other choice than modifying the code. Specificities in parameters will do query maintainability very hard and error prone. Personally, I would implement the need by allowing the client to select for each field if a condition should be applied.
Then from the implementation side, I would use this user information to build my CriteriaQuery.
And there Criteria will do an excellent job : less code duplication, more adaptability for the query building and in addition more type-checks at compile type.
Spring-data repositories use EntityManager beneath. Repository classes are just another layer for the user not to worry about the details. But if a user wants to get his hands dirty, then of course spring wouldn't mind.
That is when you can use EntityManager directly.
Let us assume you have a Repository Class like AbcRepository
interface AbcRepository extends JpaRepository<Abc, String> {
}
You can create a custom repository like
interface CustomizedAbcRepository {
void someCustomMethod(User user);
}
The implementation class looks like
class CustomizedAbcRepositoryImpl implements CustomizedAbcRepository {
#Autowired
EntityManager entityManager;
public void someCustomMethod(User user) {
// You can build your custom query using Criteria or Criteria Builder
// and then use that in entityManager methods
}
}
Just a word of caution, the naming of the Customized interface and Customized implementating class is very important
In last versions of Spring Data was added ability to use JPA Criteria API. For more information see blog post https://jverhoelen.github.io/spring-data-queries-jpa-criteria-api/ .
I have an issue where I have only one database to use but I have multiple servers where I want them to use a different table name for each server.
Right now my class is configured as:
#Entity
#Table(name="loader_queue")
class LoaderQueue
I want to be able to have dev1 server point to loader_queue_dev1 table, and dev2 server point to loader_queue_dev2 table for instance.
Is there a way i can do this with or without using annotations?
I want to be able to have one single build and then at runtime use something like a system property to change that table name.
For Hibernate 4.x, you can use a custom naming strategy that generates the table name dynamically at runtime. The server name could be provided by a system property and so your strategy could look like this:
public class ServerAwareNamingStrategy extends ImprovedNamingStrategy {
#Override
public String classToTableName(String className) {
String tableName = super.classToTableName(className);
return resolveServer(tableName);
}
private String resolveServer(String tableName) {
StringBuilder tableNameBuilder = new StringBuilder();
tableNameBuilder.append(tableName);
tableNameBuilder.append("_");
tableNameBuilder.append(System.getProperty("SERVER_NAME"));
return tableNameBuilder.toString();
}
}
And supply the naming strategy as a Hibernate configuration property:
<property
name="hibernate.ejb.naming_strategy"
value="my.package.ServerAwareNamingStrategy"
/>
I would not do this. It is very much against the grain of JPA and very likely to cause problems down the road. I'd rather add a layer of views to the tables providing unified names to be used by your application.
But you asked, so have some ideas how it might work:
You might be able to create the mapping for your classes, completely by code. This is likely to be tedious, but gives you full flexibility.
You can implement a NamingStrategy which translates your class name to table names, and depends on the instance it is running on.
You can change your code during the build process to build two (or more) artefacts from one source.
To initiate a query I use :
org.apache.ibatis.session.SqlSession.seleteList("myquery");
myquery itself is defined within an XML configuration file.
With Spring JDBC as the query statements is defined with the class itself it is very easy find the query associated with a given method call. But with mybatis once I reach the query call I have to search for the argument (in this case "myquery") to discover where the actual query is defined.
Is there an easier method of finding a query associated with a method call instead of manually searching for references ? I'm thinking there isn't as since the query itself is within an XML file not a .java file and IDE maintains a "linking" of methods, constants etc between the files.
There are two ways to do that but you will need to use mapper interfaces.
The first approach is to use IDE plugin. There are several plugins for IntelliJ and some for eclipse. Plugin for IntelliJ says it has
Proxy interfaces support, "Go to Implementaion" jumps right into
mapper xml
Another way is to define queries using annotations. In this case queries will be directly in java file.
Define query in mapper interface
interface MyMapper {
#Select("Select * from myentity where id = #id")
MyEntity selectMyEntity(#Param("id") Long id);
}
And then use mapper like usually:
MyMapper mapper = session.getMapper(MyMapper.class);
MyEntity myEntity = mapper.selectMyEntity(101);
I use the org.jooq.util.DefaultGenerator during the build process to generate jOOQ classes to represent my database schema.
While the application runs, the schema is expected to change without the application knowing about it. Such changes may or may not be compatible with the already generated code.
How can I detect in runtime whether the generated code is still valid against a certain schema?
I'm looking for something like boolean stillValid = new SchemaValidator(existingGeneratedCodePath, jdbcUrl, jdbcProps).validate();
A jOOQ 3.0 solution using org.jooq.Meta
In the upcoming jOOQ 3.0, JDBC's DatabaseMetaData can be accessed in a "jOOQ way" through a new org.jooq.Meta object (implemented with feature request #1968). This object provides access to various objects of these types:
org.jooq.Catalog
org.jooq.Schema
org.jooq.Table
org.jooq.Field
org.jooq.DataType
These could be compared to your generated classes, e.g.
MY_SCHEMA.getTables().equals(create.meta().getTables())
A jOOQ 2.x solution using JDBC DatabaseMetaData
The above solution can be implemented manually, querying the Connection.getMetaData(). It'll be a bit more work, of course
A trick querying all the tables
Another simple solution would be to query all the generated tables like this:
List<Table<?>> invalidTables = new ArrayList<>();
for (Table<?> table : MY_SCHEMA.getTables()) {
try {
create.selectFrom(table).where(Factory.falseCondition()).fetch();
}
// If table names / column names change, the above query would fail
catch (DataAccessException e) {
invalidTables.add(table);
}
}
This trick would allow to detect if increments are compatible
If I were to define some function in the database (perhaps Postgres, or any other database):
create or replace function isValidCookie(ckie);
I would call it from SQL as:
select * from cookietable c where isValidCookie(c.cookie);
How can I call a custom function such as this from Hibernate?
If you want to use your custom function in HQL, you'll need to define it in appropriate Dialect
Take a look at PostgreSQLDialect (or any other, really) source, and you'll see a bunch of registerFunction() calls. You'll need to add one more :-) - for your own custom function.
You'll then have to specify your own dialect in Hibernate configuration.
As of Hibernate 5, if you don't want to depend on or customize the dialect, you can define a MetadataBuilderInitializer. For example, to use MySQL DATE_ADD with an INTERVAL from HQL, you can define a custom function called date_add_interval:
public class DateAddIntervalMetadataBuilderInitializer
implements MetadataBuilderInitializer {
#Override
public void contribute(MetadataBuilder metadataBuilder,
StandardServiceRegistry serviceRegistry) {
metadataBuilder.applySqlFunction("date_add_interval",
new SQLFunctionTemplate(DateType.INSTANCE,
"DATE_ADD(?1, INTERVAL ?2 ?3)"));
}
}
You would also need to put the name of the class in a JAR resource file called META-INF/services/org.hibernate.boot.spi.MetadataBuilderInitializer.
This approach is particularly useful when using Hibernate via a framework such as JPA and/or Spring, where the configuration is performed implicitly by the framework.