-->
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.  [ 15 posts ] 
Author Message
 Post subject: Using HBM Information for Validation
PostPosted: Tue Aug 03, 2004 11:21 am 
Newbie

Joined: Tue Aug 03, 2004 11:19 am
Posts: 2
I was considering using the HBM information for doing field validation (rather not duplicate the information). What is the easiest way to gathering this information at runtime (or compile-time)?

For a given class/field, I'd want to know what Java type it is, max length, if required, etc.

Joel


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 03, 2004 2:00 pm 
Regular
Regular

Joined: Wed Jun 30, 2004 4:02 pm
Posts: 64
I want to do the same thing, but I'm thinking of extracting the information from the xDoclet code. The only real idea I've seen so far is to have a property file containing not null, lengths, etc. and inserting them into xDoclet with a ${name.length} notation. I could then also try to generate the commons validator xml off of the same property file.

Although the datatype isn't defined in xDoclet, since it's in the java code, and a ValidatorDoclet hasn't been written yet.

Is there a best practice guideline to accomplish this?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 03, 2004 2:40 pm 
Newbie

Joined: Tue Aug 03, 2004 11:19 am
Posts: 2
We use xdoclet as well, but its getting converted to hibernate config files. The hbm's must be loaded in for hibernate to do its work, so I assumed they would be the most accessible, already in the needed format, just need to know how to access (with Spring "in the way").

The properties file solution is reasonable since it makes it Hibernate agnostic. The other problem is that just length and not-nullable is not quite enough, it still doesn't know whether it needs to validate an email address, phone number, or a minimum length (for password, for example). I would like to keep the information in one place as much as possible though.

Let me know what you come up with. Someone must of done this before though, no?

J


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 03, 2004 7:06 pm 
Pro
Pro

Joined: Tue Aug 26, 2003 8:07 pm
Posts: 229
Location: Brisbane, Australia
On our project, we define the hibernate mapping in the Javadoc and use XDoclet to turn that into HBM mapping files.

For a given property we might have something like the following:
Code:
    /**
     * Australian Registered Business Number (for foreign owned enitities)
     *
     * @hibernate:property column="t_arbn" not-null="true"
     * @hibernate:meta name="clas.optional" value ="true"
     * @hibernate:meta name="clas.minimum" value="11"
     * @hibernate:meta name="clas.maximum" value="11"
     * @hibernate:meta name="clas.expression" value="[0-9]{3} [0-9]{3} [0-9]{3}"
     */
    public java.lang.String getArbn() {
        return arbn;
    }


The back-end framework accesses the validation information for a property by asking Hibernate for it (HB loads the metadata up when it does all the other pre-processing during SessionFactory build time) with code like this:
Code:
    public Property getProperty(PersistentClass pClass, String propertyName)
    throws PersistenceLayerException {
        try {
            Property property = pClass.getProperty(propertyName);
            if( property == null ){
                throw ExceptionUtil.createPersistenceLayerException(log, res, "metadata.get_property.no_property", "the class `{0}` does not contain a mapped property named `{1}`", new Object[]{pClass.getName(), propertyName});
            }
           
            return property;
        }
        catch( HibernateException he ){
            throw ExceptionUtil.createPersistenceLayerException(log, res, "metadata.get_property.hb_error", "while getting the property", he);
        }
    }

We have a strict separation of layers in our app, so we define a "grammar" for the "property specification" that the front-end uses when asking for validation information from the back end (this actually doesn't happen often, because we do most of our validation on the back-end). The property specification looks something like "Entity.arbn".
The code to find the right class and then call the getProperty() method above is:
Code:
    /**
     * [TODO: unqualified classnames should probably be cached in a map or
     * something, rather than iterating across all the mappings each time]
     */
    public PersistentClass getUnqualifiedClassMapping(String uqClassname)
    throws PersistenceLayerException {
        Validate.notNull(uqClassname, "param uqClassname may not be null");
        try {
            PersistentClass result = null;
            for( Iterator i=hibernateConfig.getClassMappings(); i.hasNext(); ){
                PersistentClass iClass = (PersistentClass) i.next();
                String iUQClassname =
                    StringTools.sliceRemainder(iClass.getName(), ".");
                if( uqClassname.equals(iUQClassname) ){
                    if( result != null ){
                        throw handleError("the class `"+iClass.getName()+"` has the same unqualified classname as `"+result.getName()+"`, this should not be possible because it is now impossible to tell which class you mean when you say `"+uqClassname+"`", null);
                    }
                    result = iClass;
                }
            }
           
            if( result == null ){
                throw handleError("the unqualified classname `"+uqClassname+"` could not be found in the domain model", null);
            }
            return result;
        }
        catch( Exception e ){
            throw handleError("while getting the class mappings", e);
        }
    }
   
...

    public String getSingleColMapping(String attributeSpec)
    throws PersistenceLayerException {
        PersistentClass mappedClass =
            getUnqualifiedClassMapping(
                DomainGrammar.getClassnameFromPropertySpec(attributeSpec) );
        String propertyName =
            DomainGrammar.getPropertyNameFromPropertySpec(attributeSpec);
        Iterator i;
        if (propertyName.equals("id")) {
            i = mappedClass.getIdentifier().getColumnIterator();
        } else {
            i = getProperty(mappedClass, propertyName).getColumnIterator();
        }
        String value = ((net.sf.hibernate.mapping.Column)i.next()).getName();
        if (i.hasNext()) {
            throw ExceptionUtil.createPersistenceLayerException(log, res, "getSingleColMapping", "requested a single col, but has multiple cols");
        }
        return value;
    }


The only real wrinkle to this scheme is that XDoclet doesn't support the meta attribute - if you define the javadoc as above then run it through XDoclet, the meta attributes won't propogate through to the HBM mapping file.

You can use the patch attached to XDoclet jira issue XDT-819 to enable support for the meta attribute. Building XDoclet is basic, just don't try to do "dist" build - only build the core and modules from the root. And only do that once, then while you're messing with the hibernate module XDT files just do a build from the root of the Hibernate module.

This is not a "best practice" guide, it's what we do on our project.
If this info is useful to you, it might be useful to others - I'm sure they'd appreciate it if you made a wiki topic out of it :)

_________________
Cheers,
Shorn.


Top
 Profile  
 
 Post subject: Another Solution
PostPosted: Mon Jul 11, 2005 4:42 am 
Newbie

Joined: Mon Jul 11, 2005 4:25 am
Posts: 5
Hi,

i tried several ways to get the meta-data i needed.

Note:
"HibernatePersistence" is an Interface all my Entities implement.

The following method tests the given Entity for not allowed null-values and strings which are too long.

I use two kinds of acces to the HB-Mapping- and Meta-Data, because i wasn't able to get all information from one of them:
Code:
ClassMetadata meta = HibernateTools.getSessionFactory().getClassMetadata(entity.getClass());

AND
Code:
PersistentClass classMapping = HibernateTools.getConfiguration().getClassMapping(entity.getClass());


The method is not yet finished, i'm trying to add additional meta-information for further validation.

I'll now try to use the Meta-Tags like stolley showed it:
Quote:
* @hibernate:meta name="clas.optional" value ="true"
* @hibernate:meta name="clas.minimum" value="11"
* @hibernate:meta name="clas.maximum" value="11"
* @hibernate:meta name="clas.expression" value="[0-9]{3} [0-9]{3} [0-9]{3}"


I should now be able to generate the mapping-files and acces the information via the following code.
Code:
Map attributes = classMapping.getMetaAttributes();
// AND
attributes = property.getMetaAttributes();


See my following example:

Code:
public static void genericEntityNotNullAndLengthCheck(HibernatePersistence entity) throws HibernateException, InvalidValueException {
        ClassMetadata meta = HibernateTools.getSessionFactory().getClassMetadata(entity.getClass());
String[] propertyNames = meta.getPropertyNames();
        Object[] values = meta.getPropertyValues(entity);
        boolean[] nullAllowed = meta.getPropertyNullability();
        Type[] types = meta.getPropertyTypes();

        String entityName = entity.getEntityType();
       
        PersistentClass classMapping = HibernateTools.getConfiguration().getClassMapping(entity.getClass());
       
        Map lengthMapping = new HashMap();
        for (Iterator iter = classMapping.getTable().getColumnIterator(); iter.hasNext();) {
            Column column = (Column)iter.next();
            lengthMapping.put(column.getName().toUpperCase(), new Integer(column.getLength()));
        }

        System.out.println("------ Meta-Attributes: " + entity.getEntityType() + " --------");
        Map attributes = classMapping.getMetaAttributes();
        Set keys = attributes.keySet();
        System.out.println("Keys: " + keys.size());
        for (Iterator iter = keys.iterator(); iter.hasNext();) {
            String key = (String)iter.next();
            System.out.println(key + ": " + attributes.get(key));
        }

        for (int i = 0; i < nullAllowed.length; i++) {
            Property property = classMapping.getProperty(propertyNames[i]);
            Value pv = property.getValue();
            if (pv.isSimpleValue() && !types[i].isEntityType() && !classMapping.getIdentifierProperty().equals(pv)) {

                String name = propertyNames[i];
                String type = types[i].getName();
                Integer length = (Integer)lengthMapping.get(propertyNames[i].toUpperCase());
                Object value = values[i];
                boolean nA = nullAllowed[i];

                if (!nA && value == null) {
                    if (!"objectVersion".equalsIgnoreCase(name) && !"objectIdentifier".equalsIgnoreCase(name)) { // ObjVersion und OID ignorieren !
                        throw new NullValueException(entity.getEntityType() + "." + name + " ist nicht gesetzt! (null)");
                    }
                }

                if (value != null && "String".equalsIgnoreCase(type)) {
                    if (length == null) {
                        System.out
                                .println("__Warnung__: " + entityName + "." + name + " hat keine Längenangabe! (Nehme 255)");
                        length = new Integer(255); // default
                    }
                    if (((String)value).length() > length.intValue()) {
                        throw new InvalidValueException(entityName + "." + name + " ist länger als erlaubt! (Max: " + length + ", Länge: " + ((String)value)
                                .length() + ")");
                    }
                }

                System.out.println("------ Meta-Attributes: " + entity.getEntityType() + "." + name + " --------");
                attributes = property.getMetaAttributes();
                keys = attributes.keySet();
                System.out.println("Keys: " + keys.size());
                for (Iterator iter = keys.iterator(); iter.hasNext();) {
                    String key = (String)iter.next();
                    System.out.println(key + ": " + attributes.get(key));
                }

                // System.out.println("------------ " + i + " ------------");
                // System.out.println("Name: " + name);
                // System.out.println("Type: " + type);
                // System.out.println("Length: " + length);
                // System.out.println("Value: " + value);
                // System.out.println("Nullable: " + nA);
            }
        }
    }


Greetings Fino;


Top
 Profile  
 
 Post subject: Need help again
PostPosted: Mon Jul 11, 2005 5:02 am 
Newbie

Joined: Mon Jul 11, 2005 4:25 am
Posts: 5
Hello again,

first, i think this is wrong:
Quote:
* @hibernate:meta name="clas.optional" value ="true"
* @hibernate:meta name="clas.minimum" value="11"
* @hibernate:meta name="clas.maximum" value="11"
* @hibernate:meta name="clas.expression" value="[0-9]{3} [0-9]{3} [0-9]{3}"


There need to be "." instead of the ":" -> "hibernate.meta"

But anyway, my XDoclet does not write these Attributes to the mapping-file. What do i need to do to achieve this?

Greetings;


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 11, 2005 6:54 pm 
Pro
Pro

Joined: Tue Aug 26, 2003 8:07 pm
Posts: 229
Location: Brisbane, Australia
You should probably be thinking about using standard annotations for this kind of guff instead of XDoclet these days - JDK 1.5 and Hibernate 3 being what they are and all.

If you can't use Java standard annotations and must use XDoclet, as I said, XDoclet (at the time anyway, I'm not using XDoclet/Hibernate on my present project) didn't support the meta attribute.

You will need to get the XDoclet source from CVS and figure out how to build it (if the instructions above are not enough for you, which is quite understandable, try the XDoclet mailing lists). Once you're comfortable building XDoclet, apply the patch as outlined above. At this point the hibernate meta tages should get passed through to the HBM files with no worries.

It doesn't matter if it's a ":" or "." both will work; one might be more correct than the other though - not sure which.

_________________
Cheers,
Shorn.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 11, 2005 10:14 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 7:19 pm
Posts: 2364
Location: Brisbane, Australia
If I get a moment (hard but I will try) I will have a look at te XDoclet patch and if OK commit it.


Top
 Profile  
 
 Post subject: Got it working
PostPosted: Tue Jul 12, 2005 11:06 am 
Newbie

Joined: Mon Jul 11, 2005 4:25 am
Posts: 5
I found the XDoclet-Patch in Jira, downloaded XDoclet v1.2.3 and patched it myself (by hand -> was only one ressource-file).

Now it works fine.

I cannt use standard-annotations because we have an old system which is based on java 1.4.2. An upgrade would be a bit risky.

Eclipse says, that "." is more correct.

Greetings Fino;


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 9:54 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 7:19 pm
Posts: 2364
Location: Brisbane, Australia
Nice to get confirmation thats its still OK. I'll commit it later tonight.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 13, 2005 3:32 am 
Newbie

Joined: Mon Jul 11, 2005 4:25 am
Posts: 5
Here is the Jira-Issue:
http://opensource.atlassian.com/project ... se/XDT-819

It is commented with the following:
Comment by Paul Galbraith [17/May/05 11:47 AM]
@hibernate.meta is now supported

But, it seems to be supported in not until version XDoclet 1.3 (Fix Version/s: 1.3)

The recent version is 1.2.3.

Do what you think is appropriate ;-)

Greetings Fino;


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 13, 2005 9:16 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 7:19 pm
Posts: 2364
Location: Brisbane, Australia
I checked last night and Paul has implemented it (did not use the patch). Just get the lastest CVS head version to use it. I believe they what to name the next release 1.3.0 but that was also going to be the case when they decided a quick relase 1.2.3 was necessary. Anyway, alot of updates for HIbernate 3 have gone in recently into XDoclet 1.x series.


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 23, 2006 4:39 am 
Regular
Regular

Joined: Mon Mar 06, 2006 6:18 am
Posts: 95
Location: Bern, Switzerland
hi all

i just wanted to test the code above. But where can i find the HibernateTools class?

Code:
PersistentClass classMapping = HibernateTools.getConfiguration().getClassMapping(entity.getClass());



I'm using hibernate 3.1 and hibernate entity manager 3.1.0 beta 8

Regards
Angela


Last edited by angela on Tue May 23, 2006 4:50 am, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Tue May 23, 2006 4:49 am 
Regular
Regular

Joined: Mon Mar 06, 2006 6:18 am
Posts: 95
Location: Bern, Switzerland
sorry duplicate post


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 23, 2006 7:32 pm 
Pro
Pro

Joined: Tue Aug 26, 2003 8:07 pm
Posts: 229
Location: Brisbane, Australia
I'd say that's a utility class specific to that project.

The Hibernate API doesn't give you a way of finding the Configuration object you used to create the SessionFactory, it's up to you to store it somewhere so you can use it later.

_________________
Cheers,
Shorn.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 15 posts ] 

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.