关于Hibernate的查询从数据库映射到JavaBean

Hibernate除了HQL外,还支持SQL的查询,API为createSQLQuery(sql),如果数据库使用的是Oracle,
由于数据库表中的列都是大写,所以在从resultset到javabean的时候,需要完全匹配。

一般我们会用DTO或者作为DTO的Entity,无论是采用addEntity(Class class)还是

setResultTransformer(new AliasToBeanResultTransformer (CatDTO.class))

都会遇到数据库字段到Java的大小写映射的问题,如果数据库字段是小写的id, 数据库里面是大写的ID,
则会遇到org.hibernate.PropertyNotFoundException: Could not find setter for ID on class com....的问题。

通过源码发现,要求java的属性要和数据库的字段名大小写一样,并且全匹配。

这个可以通过模仿Spring的类似查询解决。Spring的NamedJdbcTemplate有如下方法:namedJdbcTemplate.query(sql, params, new BeanPropertyRowMapper(clazz))

也是通过执行sql并把返回的结果转换成Java的方法,这个就可以忽视数据库字段的大小写问题,
仿照这个BeanPropertyRowMapper写一个适用于Hibernate的Transformer。

代码如下:


public class BeanTransformerAdapter<T> implements ResultTransformer {

/** Logger available to subclasses */

protected final Log                     logger              = LogFactory.getLog(getClass());

/** The class we are mapping to */

private Class<T>                        mappedClass;

/** Whether we‘re strictly validating */

private boolean                         checkFullyPopulated             = false;

/** Whether we‘re defaulting primitives when mapping a null value */

private boolean                         primitivesDefaultedForNullValue = false;

/** Map of the fields we provide mapping for */

private Map<String, PropertyDescriptor> mappedFields;

/** Set of bean properties we provide mapping for */

private Set<String>                     mappedProperties;

/**

* Create a new BeanPropertyRowMapper for bean-style configuration.

@see  #setMappedClass

@see  #setCheckFullyPopulated

*/

public BeanTransformerAdapter() {

}

/**

* Create a new BeanPropertyRowMapper, accepting unpopulated properties

* in the target bean.

* <p>Consider using the {@link #newInstance} factory method instead,

* which allows for specifying the mapped type once only.

@param  mappedClass the class that each row should be mapped to

*/

public BeanTransformerAdapter(Class<T> mappedClass) {

initialize(mappedClass);

}

/**

* Create a new BeanPropertyRowMapper.

@param  mappedClass the class that each row should be mapped to

* @param checkFullyPopulated whether we‘re strictly validating that

* all bean properties have been mapped from corresponding database fields

*/

public BeanTransformerAdapter(Class<T> mappedClass, boolean checkFullyPopulated) {

initialize(mappedClass);

this.checkFullyPopulated = checkFullyPopulated;

}

/**

* Set the class that each row should be mapped to.

*/

public void setMappedClass(Class<T> mappedClass) {

if (this.mappedClass == null) {

initialize(mappedClass);

} else {

if (!this.mappedClass.equals(mappedClass)) {

throw new InvalidDataAccessApiUsageException("The mapped class can not be reassigned to map to " 
+ mappedClass + " since it is already providing mapping for " + this.mappedClass);

}

}

}

/**

* Initialize the mapping metadata for the given class.

* @param mappedClass the mapped class.

*/

protected void initialize(Class<T> mappedClass) {

this.mappedClass = mappedClass;

this.mappedFields = new HashMap<String, PropertyDescriptor>();

this.mappedProperties = new HashSet<String>();

PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass);

for (PropertyDescriptor pd : pds) {

if (pd.getWriteMethod() != null) {

this.mappedFields.put(pd.getName().toLowerCase(), pd);

String underscoredName = underscoreName(pd.getName());

if (!pd.getName().toLowerCase().equals(underscoredName)) {

this.mappedFields.put(underscoredName, pd);

}

this.mappedProperties.add(pd.getName());

}

}

}

/**

* Convert a name in camelCase to an underscored name in lower case.

* Any upper case letters are converted to lower case with a preceding underscore.

* @param name the string containing original name

* @return the converted name

*/

private String underscoreName(String name) {

if (!StringUtils.hasLength(name)) {

return "";

}

StringBuilder result = new StringBuilder();

result.append(name.substring(0, 1).toLowerCase());

for (int i = 1; i < name.length(); i++) {

String s = name.substring(i, i + 1);

String slc = s.toLowerCase();

if (!s.equals(slc)) {

result.append("_").append(slc);

} else {

result.append(s);

}

}

return result.toString();

}

/**

* Get the class that we are mapping to.

*/

public final Class<T> getMappedClass() {

return this.mappedClass;

}

/**

* Set whether we‘re strictly validating that all bean properties have been

* mapped from corresponding database fields.

* <p>Default is {@code false}, accepting unpopulated properties in the

* target bean.

*/

public void setCheckFullyPopulated(boolean checkFullyPopulated) {

this.checkFullyPopulated = checkFullyPopulated;

}

/**

* Return whether we‘re strictly validating that all bean properties have been

* mapped from corresponding database fields.

*/

public boolean isCheckFullyPopulated() {

return this.checkFullyPopulated;

}

/**

* Set whether we‘re defaulting Java primitives in the case of mapping a null value

* from corresponding database fields.

* <p>Default is {@code false}, throwing an exception when nulls are mapped to Java primitives.

*/

public void setPrimitivesDefaultedForNullValue(boolean primitivesDefaultedForNullValue) {

this.primitivesDefaultedForNullValue = primitivesDefaultedForNullValue;

}

/**

* Return whether we‘re defaulting Java primitives in the case of mapping a null value

* from corresponding database fields.

*/

public boolean isPrimitivesDefaultedForNullValue() {

return primitivesDefaultedForNullValue;

}

/**

* Initialize the given BeanWrapper to be used for row mapping.

* To be called for each row.

* <p>The default implementation is empty. Can be overridden in subclasses.

* @param bw the BeanWrapper to initialize

*/

protected void initBeanWrapper(BeanWrapper bw) {

}

/**

* Retrieve a JDBC object value for the specified column.

* <p>The default implementation calls

* {@link JdbcUtils#getResultSetValue(java.sql.ResultSet, int, Class)}.

* Subclasses may override this to check specific value types upfront,

* or to post-process values return from {@code getResultSetValue}.

* @param rs is the ResultSet holding the data

* @param index is the column index

* @param pd the bean property that each result object is expected to match

* (or {@code null} if none specified)

* @return the Object value

* @throws SQLException in case of extraction failure

* @see org.springframework.jdbc.support.JdbcUtils#getResultSetValue(java.sql.ResultSet, int, Class)

*/

protected Object getColumnValue(ResultSet rs, int index, PropertyDescriptor pd) throws SQLException {

return JdbcUtils.getResultSetValue(rs, index, pd.getPropertyType());

}

/**

* Static factory method to create a new BeanPropertyRowMapper

* (with the mapped class specified only once).

* @param mappedClass the class that each row should be mapped to

*/

public static <T> BeanPropertyRowMapper<T> newInstance(Class<T> mappedClass) {

BeanPropertyRowMapper<T> newInstance = new BeanPropertyRowMapper<T>();

newInstance.setMappedClass(mappedClass);

return newInstance;

}

@Override

public Object transformTuple(Object[] tuple, String[] aliases) {

T mappedObject = BeanUtils.instantiate(this.mappedClass);

BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject);

initBeanWrapper(bw);

Set<String> populatedProperties = (isCheckFullyPopulated() ? new HashSet<String>() : null);

for (int i = 0; i < aliases.length; i++) {

String column = aliases[i];

PropertyDescriptor pd = this.mappedFields.get(column.replaceAll(" ", "").toLowerCase());

if (pd != null) {

try {

Object value = tuple[i];

try {

bw.setPropertyValue(pd.getName(), value);

} catch (TypeMismatchException e) {

if (value == null && primitivesDefaultedForNullValue) {

logger.debug("Intercepted TypeMismatchException for column " + column + " and column ‘"
 + column + "‘ with value " + value + " when setting property ‘" + pd.getName() + "‘ of type " + pd.getPropertyType() 
+ " on object: " + mappedObject);

} else {

throw e;

}

}

if (populatedProperties != null) {

populatedProperties.add(pd.getName());

}

} catch (NotWritablePropertyException ex) {

throw new DataRetrievalFailureException("Unable to map column " + column 
+ " to property " + pd.getName(), ex);

}

}

}

if (populatedProperties != null && !populatedProperties.equals(this.mappedProperties)) {

throw new InvalidDataAccessApiUsageException("Given ResultSet does not contain all fields "
 + "necessary to populate object of class [" + this.mappedClass + "]: " + this.mappedProperties);

}

return mappedObject;

}

@Override

public List transformList(List list) {

return list;

}

使用方式如下:

Query query = getSession().createSQLQuery(sql).setResultTransformer(new BeanTransformerAdapter(entityClass));

就可以不用管Oracle字段的大写问题了,会匹配到java的对应字段。

时间: 2024-10-06 20:23:49

关于Hibernate的查询从数据库映射到JavaBean的相关文章

Hibernate初探之单表映射——通过Hibernate API编写访问数据库的代码

编写一个Hibernate例子 第五步:通过Hibernate API编写访问数据库的代码 初始化方法要实现以下功能: 原文地址:https://www.cnblogs.com/songsongblue/p/9523201.html

对象序列化和反序列--Hibernate的查询和新增极其相似

Hibernate几个关键字持久化,ORM(关系对象映射)(数据库中关系称作是一张表) 应用在项目中,刘一从写的查询代码,每次都挂掉,想要弄出测试数据,自己想着把查询出来的复杂数据弄到文件里自己要是去造那些复杂数据很麻烦public class Object1 { public static void main(String args[]){ HashMap<String, Object> obj=new HashMap<String,Object>(); obj.put(&quo

Hibernate HQL查询:

Hibernate HQL查询:Criteria查询对查询条件进行了面向对象封装,符合编程人员的思维方式,不过HQL(Hibernate Query Lanaguage)查询提供了更加丰富的和灵活的查询特性,因此Hibernate将HQL查询方式立为官方推荐的标准查询方式,HQL查询在涵盖Criteria查询的所有功能的前提下,提供了类似标准SQL语句的查询方式,同时也提供了更加面向对象的封装.完整的HQL语句形势如下:Select/update/delete…… from …… where …

Hibernate学习---第三节:映射配置文件详解

1.映射文件,代码如下: <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- 持久化映射文件(将java对象映射到数据库表) default-

Hibernate数据查询(转)

Hibernate Query Language(HQL)Criteria QueryNative SQL下面对其分别进行解释Hibernate Query Language:HQL提供了是十分强大的功能,它是针对持久化对象,用取得对象,而不进行update,delete和insert等操作.而且HQL是面向对象的,具备继承,多态和关联等特性.from子句:from子句是最简单的HQL,例如from Student,也可以写成 select s from Student s.它简单的返回Stud

Hibernate(三)结构-配置文件-实体映射及配置文件

一.体系结构 SessionFactory:属于单一数据库的编译过的映射文件的一个线程安全的,不可变的缓存快照.Session的工厂.有可能持有一个可选的数据缓存可以进程级别或者群级别保存可以在事务中重用数据. 会话,Session:单线程,生命期短促的对象,代表应用程序和持久化层之间的一次对话.封装了一个JDDBC连接,它也是Transaction的工厂,保存有必须持久化对象的缓存,用于遍历对象,或者通过标识符查找对象. 持久化对象(Persistent Object)及其集合(Collect

[原创]java WEB学习笔记78:Hibernate学习之路---session概述,session缓存(hibernate 一级缓存),数据库的隔离级别,在 MySql 中设置隔离级别,在 Hibernate 中设置隔离级别

本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 -----------------------------------------------------------------------------------------------------------------

千山万水之Hibernate(四)——关联映射(多对一)

在上一篇文章(<千山万水之Hibernate(三)--基本映射 >)中,我们介绍了怎样通过Hibernate进行最基本的单实体映射以及Hibernate设计的一些基本原理,本篇文章将介绍关联映射中的多对一映射是如何实现的. 原理分析 我们拿学生和班级为示例,学生属于某一个班级,而且多个学生是有可能属于同一个班级的,相应的实体关系图为: Class的映射文件向上一篇我们介绍到的基本映射文件那样编写便可以,而对于Student实体,我们需要从Student中得出对应班级,而Student与Clas

Hibernate HQL查询语句总结

Hibernate HQL查询语句总结 1. 实体查询:有关实体查询技术,其实我们在先前已经有多次涉及,比如下面的例子:String hql="from User user ";List list=session.CreateQuery(hql).list();上面的代码执行结果是,查询出User实体对象所对应的所有数据,而且将数据封装成User实体对象,并且放入List中返回.这里需要注意的是,Hibernate的实体查询存在着对继承关系的判定,比如我们前面讨论映射实体继承关系中的E