-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 16 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: EnumType type guessing problem in Oracle
PostPosted: Fri Nov 11, 2005 9:29 am 
Newbie

Joined: Mon Nov 01, 2004 6:46 am
Posts: 6
Hi,

using the latest Hibernate Annotations and Oracle 10g ( w/ the ojdbc14.jar driver), I have problems using EnumType:

We have a persistent class (Entity) including an Enum-typed property (let' call it "foobar")

Hibernate calls

Code:
rs = metadata.getColumns(catalog, schema, table, column);


on line 156 in EnumType (column is set to its correct value, which would be (lowercase!) "foobar". Oracle tries to look it up in the table "all_tab_columns" with

Code:
SELECT [...] FROM all_tab_columns t
WHERE t.owner LIKE :1 ESCAPE '/'
  AND t.table_name LIKE :2 ESCAPE '/'
  AND t.column_name LIKE :3 ESCAPE '/'


The problem is, the column names are stored uppercased, so the t.column_name LIKE 'foobar' fails.

I think the preferred fix for this would be a "LIKE UPPER(:3)" in the Oracle driver (?), but I think it's easier to work around in Hibernate :) Using Oracle, EnumType could convert "column" to uppercase, this would solve the problem, I guess.

OK, for sure the easiest work around is to do a
Code:
@Column(name="FOOBAR")

for this property in the entity class, but I think that's verbose, unnecessary and not very nice :D

Sebastian


Top
 Profile  
 
 Post subject:
PostPosted: Fri Nov 11, 2005 2:57 pm 
Expert
Expert

Joined: Sat Jun 12, 2004 4:49 pm
Posts: 915
you can use DatabaseMetadat#storesLowerCaseIdentifiers() (storesUpperCaseIdentifiers,
storesMixedCaseIdentifiers) for this case and you haven't to use Dialect
However,one can make table with quote or jdbc driver don't implement this method correct, for example
create table "AbCd"
and it fail

the best way is hibernate way
@Column(name="FOOBAR")
or what is in your schema


Top
 Profile  
 
 Post subject:
PostPosted: Sat Nov 12, 2005 4:27 am 
Newbie

Joined: Mon Nov 01, 2004 6:46 am
Posts: 6
That's true, but a solution working out of the box would be better --
using mixed-case table or column names (at least in Oracle) seems weird to me, so that shouldn't be a point here... :P

If you create a plain new entity class containing an Enum-typed property, generate the schema via Hibernate Tools and test, you get an error (or at least warnings w/ nasty exception messages in your log) using Oracle - that's avoidable IMHO.

Sebastian


Top
 Profile  
 
 Post subject:
PostPosted: Sat Nov 12, 2005 8:53 am 
Expert
Expert

Joined: Sat Jun 12, 2004 4:49 pm
Posts: 915
Quote:
That's true, but a solution working out of the box would be better --
using mixed-case table or column names (at least in Oracle) seems weird to me, so that shouldn't be a point here... :P

i agree, but what if one do it ?
hibernate do it this good (i think)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 15, 2005 7:48 am 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
1. Open a case to Oracle for them to fix their bug
2. I can't really apply an upper case cause other DBs might work correctly and what if I do have foobar and FOOBAR in the same schema
3. You can now use (in cvs HEAD) a naming strategy with annotations, you'll be able to apply the uppercase.

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 15, 2005 8:54 am 
Expert
Expert

Joined: Sat Jun 12, 2004 4:49 pm
Posts: 915
Quote:
1. Open a case to Oracle for them to fix their bug

this isn't bug - oracle save identifiers in upper case simple - postgresql do it in lower case and it doesn't work when you type FOOBAR
in jdbc DatabaseMetadata exists method for identifier saving, but it isn't enough (i think)
hibernate do good job for this


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 15, 2005 2:16 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
Would you try to enhance the EnumType in Hibernate Annotatiions by using the storesUpperCaseIdentifiers() and so on?
Tell me if you're interested, this is probably a pretty quick patch.

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 15, 2005 3:51 pm 
Expert
Expert

Joined: Sat Jun 12, 2004 4:49 pm
Posts: 915
Emmanuel,
I don't think that it is good idea - it is possible save identifier with quotes and it isn't work then
it is better that user choose mapping or that you try add parameter to EnumType for using/not using storesUpperCaseIdentifiers()/storesLowerCaseIdentifiers() with any default value


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 15, 2005 3:55 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
You just need to know if the column is quoted or not (and we have the information already)

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 15, 2005 4:03 pm 
Expert
Expert

Joined: Sat Jun 12, 2004 4:49 pm
Posts: 915
Quote:
You just need to know if the column is quoted or not (and we have the information already)

fine, but please add parameter (boolean) for using/non using for broken jdbc drivers


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 16, 2005 5:25 am 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
So I don't get it.
Does storesUpperCaseIdentifiers()/storesLowerCaseIdentifiers() supposed to influence the way types should be requested through DatabaseMetadata?

If not, I'm lost
If yes, this means Oracle driver is right here, so which driver is broken?

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 16, 2005 9:23 am 
Expert
Expert

Joined: Sat Jun 12, 2004 4:49 pm
Posts: 915
Quote:
so which driver is broken?

i think that any driver don't implement this methods or have broken implementation - i suppose that exists one

Quote:
Does storesUpperCaseIdentifiers()/storesLowerCaseIdentifiers() supposed to influence the way types should be requested through DatabaseMetadata?

i think no
you call storesUpperCaseIdentifiers() only and if it true then identifiers in database are in upper case (for oracle this is true, for postgresql storesLowerCaseIdentifiers() is true)


Top
 Profile  
 
 Post subject: I have a working EnumType, should I open a JIRA bug?
PostPosted: Sun Nov 20, 2005 9:32 am 
Newbie

Joined: Sat Nov 19, 2005 8:07 pm
Posts: 8
emmanuel wrote:
So I don't get it.
Does storesUpperCaseIdentifiers()/storesLowerCaseIdentifiers() supposed to influence the way types should be requested through DatabaseMetadata?

If not, I'm lost
If yes, this means Oracle driver is right here, so which driver is broken?


I posted a similar issue yesterday, over in the EJB3 forum:
http://forum.hibernate.org/viewtopic.ph ... 55#2272455

I have a working solution. Let me attempt to explain how I see the problem, and then show you the relevant piece of code that fixes it.

The DatabaseMetadata has several methods that allow you to work out how the database stores identifiers. storesLowerCaseIdentifiers() and storesUpperCaseIdentifiers() tell you in effect how the database handles unquoted mixed-case identifiers. supportsMixedCaseQuotedIdentifiers() and it's siblings tells you whether the database will store identifers precicely if they are quoted.

The Oracle driver is not broken. In Oracle, identifiers are case sensitive. Most people don't know this because if you don't quote your identifiers in DDL then Oracle will upper-case them (and everyone is extremely used to this). But if you quote them, then Oracle will store the identifier as is. And two identifiers in different cases are not equivelant. This is all a bit theoretical because at the end of the day I've never met anyone (though I'm sure they're out there) that quotes identifiers in Oracle, or uses anything but full upper case. But it can be done. Check the following output from SQL-Plus:

Code:
SQL> desc test
Name       Null?    Type
---------- -------- ------
TEST_COL            NUMBER
test_col            NUMBER


The safest/best algorithm, it seems to me, is to hit the meta data multiple times. First time using the exact identifiers handed to the EnumType (that way, you can always be assured a correct answer if you use @Column to annotate the exact column name). If that fails, and the database stores unquoted identifiers as upper-case, try again using upper case identifiers. If that fails and the database stores unquoted identifiers as lower case, try again using lower case identifiers.

There is the chance here that is someone uses the same name, in different case, in the same table that it will produce a bogus answer. But that can be worked around with @Column annotations, or by shooting the developer who thought that'd be a good idea ;)

I can post this as a real patch, but to give you a better idea of what it looks like, here's the reworked method:

Code:
    private int guessTypeFromMetaData(DatabaseMetaData metadata) throws SQLException {
        ResultSet rs = null;
        try {
            String username = metadata.getUserName();
            int guessedType = Types.NULL;
            int numResults = 0;
            boolean nonNullType = false;

            // Try to find the column information up to three time. First with values as is in case
            // the database ever stores mixed case identifiers, or the supplied identifiers match
            // perfectly. Then if the metadata is not found, try using lower and upper case
            // identifiers - but only if the datbaase reports it would use them.
            for (int i=0; i<3 && numResults == 0; ++i) {
                if (i == 0) { // First try with the case that was supplied
                    rs = metadata.getColumns(catalog, schema, table, column);
                }
                else if (i == 1 && metadata.storesLowerCaseIdentifiers()) {
                    rs = metadata.getColumns(catalog == null ? catalog : catalog.toLowerCase(),
                                             schema == null  ? schema  : schema.toLowerCase(),
                                             table.toLowerCase(), column.toLowerCase());
                }
                else if (i == 2 && metadata.storesUpperCaseIdentifiers()) {
                    rs = metadata.getColumns(catalog == null ? catalog : catalog.toUpperCase(),
                                             schema == null  ? schema  : schema.toUpperCase(),
                                             table.toUpperCase(), column.toUpperCase());
                }

                // try to find
                while ( rs.next() ) {
                    guessedType = rs.getInt( "DATA_TYPE" );
                    if ( rs.wasNull() ) {
                        continue; // TODO throw exception?
                    }
                    else {
                        nonNullType = true;
                    }

                    numResults++;
                    if ( username != null) {
                        String foundSchema = rs.getString( "TABLE_SCHEM" );
                        // if the username matches the schema, there's no better guess available...
                        if ( username.equalsIgnoreCase(foundSchema) ) return guessedType;
                    } else if ( numResults > 1 ) {
                        // if there's no username and multiple results, there's no point in continuing
                        throw new HibernateException("Several columns matching in metadata: " + column);
                    }
                }
            }

            if (numResults == 0) throw new HibernateException("Enum type column not found in metadata: " + column);
            if (numResults  > 1) throw new HibernateException("Several columns matching in metadata: " + column);
            if (!nonNullType) throw new HibernateException("Column without type in metadata!: " + column);
            log.trace( "Enum type guessed from metadata: " + guessedType);
            return guessedType;
        } finally {
            try {
                if (rs != null) rs.close();
            } catch (SQLException e) {
                //swallow in purpose
            }
        }
    }

_________________
-Tim Fennell
Stripes: Because web development should just be easier.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Nov 20, 2005 12:57 pm 
Expert
Expert

Joined: Sat Jun 12, 2004 4:49 pm
Posts: 915
Quote:
The Oracle driver is not broken.
Code:
i don't tell that is broken - and i tell for no one driver
i suppose that exists one or don't implement this methods

you example for oracle is same for postgresql, but opposite - they save identifiers in lower case


Top
 Profile  
 
 Post subject:
PostPosted: Sun Nov 20, 2005 3:10 pm 
Newbie

Joined: Mon Nov 01, 2004 6:46 am
Posts: 6
Tim, thank you for your "patch" - I really think we do need a solution to make EnumType work w/o any hassle...

I don't really understand your for-loop, though (an I think it could mess up the local vars...) Why don't use something like that?

Code:
rs = metadata.getColumns(catalog, schema, table, column);

// rs empty? check for lc ids
if (!rs.isBeforeFirst() && metadata.storesLowerCaseIdentifiers()) {
rs = metadata.getColumns(catalog == null ? catalog : catalog.toLowerCase(),
                                             schema == null  ? schema  : schema.toLowerCase(),
                                             table.toLowerCase(), column.toLowerCase());
                }
// rs still empty? try uc ids
if (!rs.isBeforeFirst() && metadata.storesUpperCaseIdentifiers()) {
                    rs = metadata.getColumns(catalog == null ? catalog : catalog.toUpperCase(),
                                             schema == null  ? schema  : schema.toUpperCase(),
                                             table.toUpperCase(), column.toUpperCase());
                }

...



Don't know if this works (I'm kinda rusty w/ direct JDBC code because of Hibernate *LOL*), but it looks a little bit more straight forward... :)

Sebastian


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 16 posts ]  Go to page 1, 2  Next

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.