fujy
fujy

Reputation: 5264

Hibernate: Set field by its column name

Is there a way to set some field in an entity with only an information for the field column name?

for example:

@Entity
@Table(name = "T_PERSON")
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "p_id")
    private int id;

    @Column(name = "p_name")
    private String name;

    // getters and setters for id and name
}

So I have p_name and T_PERSON as an Input and I want to set some value in it.

I found this and this to get field names using column names, and I could use Reflection to get the setter method, but Are there any alternatives to reflection?

Upvotes: 3

Views: 10682

Answers (5)

Ravindra HV
Ravindra HV

Reputation: 2608

Since the method to be called is not known at compile time, as per my understanding, reflection is the only way.

If you are trying to avoid having to use reflection in the invoking code, you can write a wrapper method that internally uses reflection to do the setting and then use that method in the invoking method.

Something like this :

public static <T> Map<String, java.lang.reflect.Method> obtainMapOfColumnNamesAndSetters(Class<T> classType) {
     Map<String, java.lang.reflect.Method> result = null;

     /*
      * Use reflection to iterate over all fields and 
      * identify ones with annotation and build a map of database column names and fields 
      * and use the field names to obtain the setter method references and save it in a map and return it as result.
      * 
      * The result map's key is the column name and the value is the method to be set.
      * 
      * Note: Ideally, this map needs to be build only once per class.
      */
    return result;
}

public static boolean genericHibernatePropertySetter(Object beanObject, Object parameterObject, String annotationValue, 
        Map<String, java.lang.reflect.Method> columnAndMethodMap) 
{
    boolean result = false;

    /*
     * Use this method to hide the reflection from invoking classes.
     *        
     * Fetch the corresponding 'java.lang.reflect.Method' instance from the map. 
     * Use the method instance to set the parameter on the bean object.
     * 
     */

    return result; // set to true in the implementation

}

Hope this helps.

Upvotes: 0

Shailendra
Shailendra

Reputation: 9102

I think your best bet would be using reflection only. Hibernate internally too uses reflection to read all the mappings done via annotations. If you want to try out the hibernate internal classes then have a look at the SessionFactory implementation class of Hibernate (org.hibernate.impl.SessionFactoryImpl), it holds map containing class metadata (org.hibernate.metadata.ClassMetadata) for each entity. You already must be having a reference to the singleton SessionFactory in your code.

You can get hold of ClassMeta data with

public ClassMetadata getClassMetadata(Class persistentClass) throws HibernateException 

There are few methods in ClassMetaData which could be of your interest.For e.g.,

public void setPropertyValue(Object object, String propertyName, Object value, EntityMode entityMode) throws HibernateException;

EntityMode can be specified as EntityMode.POJO

Also if you have a reference of Configuration object used for initializing hibernate you can query for the table you are interested in as

public Table getTable(String schema, String catalog, String name) {
            String key = Table.qualify(catalog, schema, name);
            return tables.get(key);
        }

and there are methods to get the physical or logical column names

public String getPhysicalColumnName(String logicalName, Table table) throws MappingException

and

public String getLogicalColumnName(String physicalName, Table table) throws MappingException

Upvotes: 4

Timofey Gorshkov
Timofey Gorshkov

Reputation: 5125

You could run common SQL update query using method like this:

public int updateSomeParameter(String tableName, String idColumnName, int id,
                               String parameterColumnName, Object newParameterValue) {
    String sql = "UPDATE " + tableName + " SET " + parameterColumnName
                 + " = :new_value WHERE " + idColumnName + " = :id";
    SQLQuery query = session.createSQLQuery(sql);
    query.setInteger("id", id);
    query.setParameter("new_value", newParameterValue);
    return query.executeUpdate();
}

int updateCout = updateSomeParameter("T_PERSON", "p_id", 50, "p_name", "Some New Name");

But this way is highly unrecommended, especially if table and column names come from user input, because it bocomes SQL injection prone.

Upvotes: 0

If you have control over Entity class property name creation then create java property name based on table column name(Apply Rule(s) for eg : Rule 1 - for column p_name as pName).

Apply the rule on user input table column name and identify the entity class property and call the setter method.

If dont have control over Entity class property name then using reflection is the best way.

Upvotes: 0

Jason Lowenthal
Jason Lowenthal

Reputation: 810

While I generally agree with the comments - I'd use JDBCTemplate or some such thing to accomplish what you're after, you might look into @Transient annotations on a field, or possibly just writing something like:

public void setFieldForColumnName(String columnName, Object value){
    Switch(columnName){
        case("columnA"):
            setColumnA(value);
            break;
        default:
            // ....
            break;
    }
}

Of course, this relies on a string switch - you'd have to convert to if/then if you don't have the ability to switch on strings.

I reiterate, I don't think this is really the ideal way to manage this, but if you are absolutely married to hibernate, this would be one way to do what you want (I Think)

Upvotes: 0

Related Questions