Java 在 Hibernate 中使用 Transformers.aliasToBean 填充子 bean
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18713235/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Populate child bean with Transformers.aliasToBean in Hibernate
提问by maqjav
I have the next couple of beans:
我有接下来的几个豆子:
Address {
String name;
String number;
String zipcode;
String town;
}
MyEntity {
Address address;
String value1;
String value2;
}
I'm trying to do the next Hibernate query:
我正在尝试执行下一个 Hibernate 查询:
private final List<String> propertiesDistinct = Arrays.asList("address.name");
private final List<String> properties = Arrays.asList("address.number",
"address.zipcode", "address.town")
ProjectionList projectionList = Projections.projectionList();
if (propertiesDistinct != null) {
ProjectionList projectionListDistinct = Projections.projectionList();
for (String propertyDistinct : propertiesDistinct)
projectionListDistinct.add(Projections.property(propertyDistinct).as(propertyDistinct));
projectionList.add(Projections.distinct(projectionListAgrupar));
}
if (properties != null)
for (String property : properties)
projectionList.add(Projections.property(property).as(property));
criterio.setProjection(projectionList);
// MORE FILTERS ON MyEntity FIELDS
//... criterio.add(Restrinctions...);
// I want to recover the results on my bean MyEntity so I don't have to create a new one
criterio.setResultTransformer(Transformers.aliasToBean(MyEntity.class));
Problem:
问题:
Caused by: org.hibernate.PropertyNotFoundException: Could not find setter for address.name on class com.entities.MyEntity
I understand that Hibernate is looking for something like:
我知道 Hibernate 正在寻找类似的东西:
public String getAddressName() {} // This should be in MyEntity
Instead of:
代替:
public String getName() {} // In my Address bean
Ideas about how can I fix this without creating a new bean?
关于如何在不创建新 bean 的情况下解决此问题的想法?
Thanks!
谢谢!
采纳答案by Sami Andoni
回答by Ean V
Try creating an alias like criterio.createAlias("address", "add");
and then edit your properties to be like Arrays.asList("add.number","add.zipcode", "add.town")
.
尝试创建别名 likecriterio.createAlias("address", "add");
然后将您的属性编辑为 like Arrays.asList("add.number","add.zipcode", "add.town")
。
Hope this helps.
希望这可以帮助。
回答by Vicky Thakor
Code provided in Githubworks fine but there is change in import
for new versions of hibernate. Its as follow.
Github 中提供的代码工作正常,但import
新版本的 hibernate发生了变化。它如下。
org.hibernate.property.PropertyAccessor
replaced byorg.hibernate.property.access.spi.PropertyAccess
org.hibernate.property.PropertyAccessor
取而代之org.hibernate.property.access.spi.PropertyAccess
and
和
org.hibernate.property.PropertyAccessorFactory
replaced by org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl
org.hibernate.property.PropertyAccessorFactory
取而代之 org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl
So you'll have change the code from
所以你必须改变代码
PropertyAccessor accessor = PropertyAccessorFactory.getPropertyAccessor("property");
accessor.getSetter(resultClass, (String)subclassToAlias.get(subclass).get(2)).set(root, subObject, null);
to
到
PropertyAccess propertyAccess = PropertyAccessStrategyBasicImpl.INSTANCE.buildPropertyAccess(resultClass, (String)subclassToAlias.get(subclass).get(2));
propertyAccess.getSetter().set(root, subObject, null);
回答by Ronny Shibley
AliasToBeanNestedResultTransformer does not handle Nested Multi Level DTOs, so I rewrote one that handles n-level dtos.
AliasToBeanNestedResultTransformer 不处理嵌套多级 DTO,所以我重写了一个处理 n 级 dto。
Hope this helps.
希望这可以帮助。
public class AliasToBeanNestedMultiLevelResultTransformer extends AliasedTupleSubsetResultTransformer {
private static final long serialVersionUID = -8047276133980128266L;
public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
return false;
}
private boolean initialized;
private Class<?> resultClass;
private Map<String,Class<?>> clazzMap = new HashMap<>();
private Map<String,Setter> settersMap = new HashMap<>();
public AliasToBeanNestedMultiLevelResultTransformer(Class<?> resultClass) {
this.resultClass = resultClass;
}
public Object transformTuple(Object[] tuples, String[] aliases) {
Map<String,Object> nestedObjectsMap = new HashMap<>();
Object result;
try {
result = resultClass.newInstance();
if (!initialized){
initialized = true;
initialize(aliases);
}
for (int a=0;a<aliases.length;a++){
String alias = aliases[a];
Object tuple = tuples[a];
Object baseObject = result;
int index = alias.lastIndexOf(".");
if(index>0){
String basePath = alias.substring(0, index);
baseObject = nestedObjectsMap.get(basePath);
if (baseObject == null){
baseObject = clazzMap.get(basePath).newInstance();
nestedObjectsMap.put(basePath, baseObject);
}
}
settersMap.get(alias).set(baseObject, tuple,null);
}
for (Entry<String,Object> entry:nestedObjectsMap.entrySet()){
Setter setter = settersMap.get(entry.getKey());
if (entry.getKey().contains(".")){
int index = entry.getKey().lastIndexOf(".");
String basePath = entry.getKey().substring(0, index);
Object obj = nestedObjectsMap.get(basePath);
setter.set(obj, entry.getValue(), null);
}
else{
setter.set(result, entry.getValue(), null);
}
}
}catch ( InstantiationException | IllegalAccessException e) {
throw new HibernateException( "Could not instantiate resultclass: " + resultClass.getName() );
}
return result;
}
private void initialize(String[] aliases) {
PropertyAccessor propertyAccessor = new ChainedPropertyAccessor(
new PropertyAccessor[] {
PropertyAccessorFactory.getPropertyAccessor( resultClass, null ),
PropertyAccessorFactory.getPropertyAccessor( "field" )
}
);
for (int a=0;a<aliases.length;a++){
String alias = aliases[a];
Class<?> baseClass = resultClass;
if (alias.contains(".")){
String[] split = alias.split("\.");
StringBuffer res = new StringBuffer();
for (int i=0;i<split.length;i++){
if (res.length()>0) res.append(".");
String item = split[i];
res.append(item);
String resString = res.toString();
if (i==split.length-1){
clazzMap.put(resString,baseClass);
settersMap.put(resString, propertyAccessor.getSetter(baseClass, item));
break;
}
Class<?> clazz = clazzMap.get(resString);
if (clazz==null){
clazz = propertyAccessor.getGetter(baseClass,item).getReturnType();
settersMap.put(resString, propertyAccessor.getSetter(baseClass, item));
clazzMap.put(resString,clazz);
}
baseClass = clazz;
}
}
else{
clazzMap.put(alias, resultClass);
settersMap.put(alias, propertyAccessor.getSetter(resultClass, alias));
}
}
}
}
}
回答by otonglet
My solution is very basic. It's not as clean as a proper result transformer but it's useful when you just need to do a quick projection for a few properties.
我的解决方案非常基础。它不像正确的结果转换器那么干净,但是当您只需要对几个属性进行快速投影时它很有用。
If you get Could not find setter for address.name on class com.entities.MyEntity
如果你得到 Could not find setter for address.name on class com.entities.MyEntity
It doesn't mean Hibernate is looking for public String getAddressName() {}
. Instead it looks for a setter with the impossible "setAddress.name" name.
这并不意味着 Hibernate 正在寻找public String getAddressName() {}
. 相反,它寻找具有不可能的“setAddress.name”名称的 setter。
Instead of .add(Projections.property("address.name"),"address.name"))
type a proper setter name as second argument to the .add() method as follows .add(Projections.property("address.name"),"addressName"))
而不是.add(Projections.property("address.name"),"address.name"))
键入正确的 setter 名称作为 .add() 方法的第二个参数,如下所示.add(Projections.property("address.name"),"addressName"))
Then, just add a setter on your "MyEntity" root object: "setAddressName".
然后,只需在“MyEntity”根对象上添加一个 setter:“setAddressName”。
public void setAddressName(String addressName) {
this.address= (this.address==null) ? new Address() : address;
this.address.setName(addressName);
}
The drawback is that it "dirties" your object with extra methods.
缺点是它用额外的方法“弄脏”了你的对象。
Also posted here.
也贴在这里。