Hibernate: use backticks for MySQL but not for HSQL - java

A project I work on (which uses Java, Spring, Hibernate) recently changed from Oracle to MySQL. There are a few cases where some of the properties in the code are reserved words in MySQL, such as "release".
There are a few solutions, 1) rename properties in the code and subsequent getter/setter methods, also update code that invokes those methods 2) annotate the property in the code with #Column(name="`release`"). This tells hibernate to quote the name when talking to the database.
I'd prefer to stay away from the first approach to reduce the chance of breaking more stuff. The second approach is "ok", except it becomes MySQL specific. In our dev. setup we use HSQL which doesn't like the backticks around those column names.
I looked at the org.hibernate.mapping.Column class and I see it has "getQuotedName" methods that I could potentially override if I could subclass Column and tell Hibernate to use my own Column class.
What's the best way to resolve this issue based on the preferred approach of a) not having to refactor the codebase (b/c of changing property names, getter/setter methods, etc) and b) wanting the app to still work in HSQL and MySQL.
It would be reasonable to have a property in properties file that could be toggled to switch on/off some Column naming fix. Which reminds me, I tried using a custom naming strategy and overriding the "columnName" method to surround the column name in backticks...this doesn't work, even on MySQL.

The back ticks solution sounds good. But if it does not work or you do not want to use an undocumented feature of an specific JPA providery: Why don't use column names that are not reserved in any(or the most common) databases at all.
You don't need to change the name of your java properties, you must only specify a column name for them.

Use backtips. It is your way of telling Hibernate that you want that identifier to be quoted properly. It's just a coincidence that backticks are also used as quoting character for MySQL. This backtick will be translated to whatever is the quoting character for your database. Regarding adding quotes as the default behavior, I'm not sure Hibernate have such option. Nothing in the documentation mentions a possible option, and I don't remember seeing anything like this in the test suite as well. But I don't think it's a good idea, as "reserved keywords" are exceptions, not the rule.

Related

How to map custom data structures to bean entities with JPA/Hibernate?

We have a (possibly large) custom data structure implemented in Java (8+). It has a simple and optimal API for querying pieces of data. The logical structure is roughly similar to an RDMS (it has e. g. relations, columns, primary keys, and foreign keys), but there is no SQL driver.
The main goal is to access the data via ORM (mapping logical entities to JPA annotated beans). It would be nice if we could use JPQL. Hibernate is preferred but other alternatives are welcome too.
What is the simplest way to achieve this? Which are the key parts of such an implementation?
(P. S. Directly implementing SessionImplementor, EntityManagerImplementor etc. seems to be too complicated.)
You have two possibilities.
Implement a JDBC compliant driver for your system, so you can use a JPA implementation such as Hibernate "directly" (although you may need to create a custom dialect for your system).
Program directly against the JPA specification like ObjectDB does, which bypasses the need to go through SQL and JPA implementations completely.
The latter one is probably easier, but you'd still need to implement the full JPA API. If it's a custom in-house-only system, there's very little sense in doing either one.
One idea I thought up just now, that I feel may work is this:
Use an existing database implementation like H2 and use the JPA integration with that. H2 already has a JPA integration libraries, so it should be easy.
In this database, create a Java stored procedure or function and call it from your current application through JPA. See this H2 documentation on how to create a Java stored procedure or function. (You may want to explore the section "Using a Function as a Table" also.)
Define a protocol for the service methods and encapsulate it in a model class. An instance of this model class may be passed to the function/SP and responses retrieved.
Caveat: I have never done this myself but I think it will work.
Edit: Here is a diagram representing the thought. Though the diagram show H2 separately, it will most probably be in the same JVM as "Your Java/JEE application". However, since it is not necessary to use H2, I have shown it as as separate entity.

JOOQ Metamodel: why is there no Table.rename(String)?

I am dynamically creating and using physical DB tables for which I only have one metamodel object. An example: I have one JOOQ class Customer in my metamodel, but I have CUSTOMER1, CUSTOMER2, etc. at runtime. I'd like to write strongly typed JOOQ queries for these dynamic tables. The following seems to do the trick:
Customer CUSTOMER1 = Customer.rename("CUSTOMER1")
Of course, there's a whole bunch of tables for which I need to do this. Unfortunately, I cannot leverage the rename method generically, because it is not part of the Table<R> interface. Is this an oversight or a deliberate measure against something I am missing?
Is there a robust and elegant way to achieve what I want, i.e. without resorting to reflection?
EDIT: the two tables are never used jointly in the same query. Concrete usage pattern is the following: At any given moment, a DB synonym CUSTOMER will point to one (the active), while the other is being modified (the shadow copy). Once the modification is complete, roles are swapped by pointing the synonym at the other one, and we start over. We do this to minimize "downtime" of heavy reporting result tables.
The answer to your question
The question being in the title:
why is there no Table.rename(String)?
The feature was implemented in jOOQ 3.3 (#2921). The issue reads:
As table renaming is not really a SQL DSL feature, the rename() method should be generated onto generated tables only, instead of being declared in org.jooq.Table
In fact, this argument doesn't really make much sense. There are other "non-DSL" utilities on the DSL types. I don't see why rename() is special. The method should be declared on Table as you suggested. This will be done in jOOQ 3.9 (#5242).
The answer to your problem
From the update of your question, I understand that you don't really need that fine-grained renaming control. jOOQ's out of the box multi-tenancy feature will do. This is called "table mapping" in the manual:
http://www.jooq.org/doc/latest/manual/sql-building/dsl-context/runtime-schema-mapping
For the scope of a Configuration (most fine-grained scope: per-query level), you can rewrite any matching table name as such:
Settings settings = new Settings()
.withRenderMapping(new RenderMapping()
.withSchemata(
new MappedSchema().withInput("MY_SCHEMA")
.withOutput("MY_SCHEMA")
.withTables(
new MappedTable().withInput("CUSTOMER")
.withOutput("CUSTOMER1"))));

Is it possible to make oracle database procedures to ignore commit statements?

I'm working on Java application that integrates with legacy system written Oracle PL/SQL. Unfortunately i'm not able to change this legacy system. Problem with this system is that it that sometimes COMMIT statements are written into procedures. But this causes that I'm not able to handle transactions correctly on my application level.
So is it possible to make oracle database procedures to ignore commit statements?
I have found that when doing ALTER SESSION DISABLE COMMIT IN PROCEDURE in beginning of connection will cause exception when PL/SQL procedure is trying to commit. But is it possible to make Oracle to ignore commit without changing PL/SQL code?
I don't think you can do that. You'll have to add a parameter to those procedures like"do commit"with a default value true. And you call them with parameter set to false. Pass the parameter value on, if they are nested. That way the legacy code still behaves the same but you get transaction control.
I'm working with Oracle for 9 years now. I also checked undocumented parameters regarding your question and I'm pretty sure, that there is no way to let Oracle ignore a commit of a stored procedure.
But of cause, theroretical you could use Oracle's flashback feature (e.g. flashback database or flashback table), to reset whole database or single tables to a state before your transaction began. But be aware, that this only works as desired, if you are the only one, who changes anything at the objects you flash back. This is usually unrealistic. By the way you also need to consider that the flashback feature is not designed to support such a scenario, so performance of your application will be suboptimal in case you need to flashback anything.
But this may be a way to solve your problem, if you have no other choice.
Probably the best thing is to alter the pl/sql procedures without affecting current functionality. In other words, add a new parameter to allow the user to ignore commits, but default to existing functionality (to commit). I've done this in a similar situation and it worked well.
So, you'd have something like:
create or replace procedure some_proc(
i_num in number, -- existing parameter
i_commit in number default 1) -- perform commit? 0=false, else true
as
begin
-- some DML here
if (i_commit <> 0) then
commit;
end if;
end;
Make sure that this new parameter is added to the end of the param list. So your app would pass in 0 (false) for i_commit.
Hope that helps.
I have found that when doing ALTER SESSION DISABLE COMMIT IN PROCEDURE
in beginning of connection will cause exception when PL/SQL procedure
is trying to commit.
Yeah, the documented behaviour of that statement is to force ORA-00034 exceptions if a procedure attempts to issue a commit. I think it's really intended as a testing thing, to identify procedures with embedded commits.
I think it is widely regarded as bad practice for stored procedures to issue commits. Control of the transaction must belong to the top of the calling stack.
Unfortunately there is no way to ignore those embedded commits. You will either have to re-write the PL/SQL routines, or else code some workarounds in your calling code (e.g. exception handler which issues additional DML to reverse committed changes).
Knowing what the procedure did would have been helpful.
However, assuming that the procedure modifies data in a limited number of tables(That's what we mostly do in PLSQL anyway), you could try running a flashback query on that schema:
FLASHBACK TABLE TABLE_NAME TO TIMESTAMP(TO_DATE('06-SEP-2012 23:59:59','DD-MON-YYYY HH24: MI: SS'));
You can set the time string "06-SEP-2012 23:59:59" to the time just before the procedure is called in your JAVA code.
Its a bad workaround, but worth a try I guess
I'm working on Java application that integrates with legacy system
written Oracle PL/SQL. Unfortunately i'm not able to change this
legacy system.
Hmmm, this smells like politics... "Don't touch this, it works!" I guess. :(
As a matter of fact, it's most likely impossible to ignore a COMMIT statement, as #Tilman Fliegel (and others) already said. And if it was, it would be quite an ugly wart in your codebase.
I'm not that good at politics, but I'd say if you cannot use nor change this, then just don't use this. I mean:
If you cannot change your procedures because they are used by other (old, immutable) systems, then duplicate them, then modify/refactor your copy until you're happy with it. If you're able to make your version retro-compatible, you may even provide a way for other (old) systems to use yours afterwards, when/if they are refactored: just introduce a "legacy mode" input parameter or whatever, cf. other answers.
If you cannot change code because you cannot understand/test it, then this is a major issue. It may even be safer to just trash it and start over from scratch (provided you're able to do so).
But maybe trying to ignore the COMMITs is easier after all. Humans are so hard to refactor... ;)

string decoupling and field names

I have a number of domain/business objects, which when used in a hibernate criteria are referenced by the field name as a string, for example :
Criteria crit = session.createCriteria(User.class);
Order myOrdering = Order.desc("firstname");
crit.addOrder(myOrdering);
Where firstname is a field/property of User.class.
I could manually create an Enum and store all the strings in there; is there any other way that I am missing and requires less work(I'll probably forget to maintain the Enum).
I'm afraid there is no a good way to do that.
Even if you decide to use reflections, you'll discover the problem only when the query will run.
But there is a little bit better solution how to discover the problem early: if you use the Named Queries (javax.persistence.NamedQueries) you'll get all your queries compiled as soon as your entities are processed by Hibernate, so basically it will happen during the server's start-up. So if some object was changed breaking the query, you'll know about it the next time you start the server and not when the query is actually run.
Hope it helps.
This is one of the things that irritates me about Hibernate.
In any case, I've solved this in the past using one of two mechanisms, either customizing the templates used to generate base classes from Hibernate config files, or interrogating my Hibernate classes for annotations/properties and generating appropriate enums, classes, constants, etc. from that. It's pretty straight-forward.
It adds a step to the build process, but IMO it was exactly what I needed when I did it. (The last few projects I haven't done it, but for large, multi-dev things I really like it.)

Table not created by Hibernate

I annotated a bunch of POJO's so JPA can use them to create tables in Hibernate. It appears that all of the tables are created except one very central table called "Revision". The Revision class has an #Entity(name="RevisionT") annotation so it will be renamed to RevisionT so there is not a conflict with any reserved words in MySQL (the target database).
I delete the entire database, recreate it and basically open and close a JPA session. All the tables seem to get recreated without a problem.
Why would a single table be missing from the created schema? What instrumentation can be used to see what Hibernate is producing and which errors?
Thanks.
UPDATE: I tried to create as a Derby DB and it was successful. However, one of the fields has a a name of "index". I use #org.hibernate.annotations.IndexColumn to specify the name to something other than a reserved word. However, the column is always called "index" when it is created.
Here's a sample of the suspect annotations.
#ManyToOne
#JoinColumn(name="MasterTopID")
#IndexColumn(name="Cx3tHApe")
protected MasterTop masterTop;
Instead of creating MasterTop.Cx3tHApe as a field, it creates MasterTop.Index. Why is the name ignored?
In case this helps anybody, this happened to me today and it turned out I was using a reserved word on my entity definition:
#OneToMany(mappedBy="list")
#OrderColumn(name="order")
private List<Wish> wishes;
"order" in this case, and Hibernate just skipped over this class. So check your user defined names! :)
Cheers,
Mark
Answer to your side question (What instrumentation can be used to see what Hibernate is producing and which errors?)
You can org.hibernate.tool.hbm2ddl.SchemaExport to generate your tables.
AnnotationConfiguration conf = (new AnnotationConfiguration()).configure();
new SchemaExport(conf).create(showHql, run);
The first argument allows you to see which HQL this command generates (CREATE TABLEs etc). The second one is whether it should actually perform any modifications (ie false = dry-run).
So running it with (true, false) will show you exactly what Hibernate would do to your tables, without changing anything.
name attribute on #Entity is not what you want to use for this purpose. Use #Table annotation instead:
#Entity
#Table(name="RevisionT")
public class Revision {
It is due to column names matches with your underlying Database sql reserved words.....try by changing name of columns.....i had faced same problem by changing names it did work.
For me it was a syntax mistake in columnDefinition. Easily detectable by the debug logs.
Perhaps you're using the wrong annotation. Once I accidentally annotated an entity with #org.hibernate.annotations.Entity instead of #javax.persistence.Entity, and Hibernate just skipped it.
Put hibernate.show_sql config property to true and see if Hibernate generated the create table code. If so, copy the create statement into your database client (ui or command line) and see if your database returns an error.
For me the error was not apparent until I did so.
I used the field name 'condition' which was a reserved MySQL word.
So check your field names...

Categories