SOLRJ-6.0.0:插入关联 bean 对象列表的 bean 对象给出空指针异常
Posted
技术标签:
【中文标题】SOLRJ-6.0.0:插入关联 bean 对象列表的 bean 对象给出空指针异常【英文标题】:SOLRJ-6.0.0: Insertion of a bean object which associate list of bean object is giving null pointer exception 【发布时间】:2016-09-11 12:13:22 【问题描述】:员工 Bean 类:
public class Employee2
private String id;
private String name;
private String designation;
private double salary;
private double totalExperience;
// private Address2 address2;
private Collection<Technology2> technologies2;
private String content_type = "employee2";
public Employee2()
super();
public Employee2(String id, String name, String designation, double salary, double totalExperience,
Collection<Technology2> technologies2)
super();
this.id = id;
this.name = name;
this.designation = designation;
this.salary = salary;
this.totalExperience = totalExperience;
// this.address2 = address2;
this.technologies2 = technologies2;
/**
* @return the id
*/
public String getId()
return id;
/**
* @param id the id to set
*/
@Field (value = "id")
public void setId(String id)
this.id = id;
/**
* @return the name
*/
public String getName()
return name;
/**
* @param name the name to set
*/
@Field (value = "name")
public void setName(String name)
this.name = name;
/**
* @return the designation
*/
public String getDesignation()
return designation;
/**
* @param designation the designation to set
*/
@Field (value = "designation_s")
public void setDesignation(String designation)
this.designation = designation;
/**
* @return the salary
*/
public double getSalary()
return salary;
/**
* @param salary the salary to set
*/
@Field (value = "salary_d")
public void setSalary(double salary)
this.salary = salary;
/**
* @return the totalExperience
*/
public double getTotalExperience()
return totalExperience;
/**
* @param totalExperience the totalExperience to set
*/
@Field (value = "totalExperience_d")
public void setTotalExperience(double totalExperience)
this.totalExperience = totalExperience;
// /**
// * @return the address2
// */
// public Address2 getAddress()
// return address2;
//
//
// /**
// * @param address2 the address2 to set
// */
// @Field (child = true)
// public void setAddress(Address2 address2)
// this.address2 = address2;
//
/**
* @return the technologies2
*/
public Collection<Technology2> getTechnologies2()
return technologies2;
/**
* @param technologies2 the technologies2 to set
*/
@Field (child = true)
public void setTechnologies2(Collection<Technology2> technologies2)
this.technologies2 = technologies2;
/**
* @return the content_type
*/
public String getContent_type()
return content_type;
/**
* @param content_type the content_type to set
*/
@Field(value="content_type_t")
public void setContent_type(String content_type)
this.content_type = content_type;
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
return "Employee2 [id=" + id + ", name=" + name + ", designation=" + designation + ", salary=" + salary +
", totalExperience=" + totalExperience + ", technologies2=" + this.getTechnologies(technologies2) +
", content_type=" + content_type + "]";
private String getTechnologies(Collection<Technology2> technologies2)
String strTechnologies = "[";
for(Technology2 technology: technologies2)
strTechnologies = strTechnologies+technology.toString();
return strTechnologies+"]";
科技豆类:
public class Technology2
private String id;
private String name;
private int experience;
private boolean certified;
private String content_type = "technology2";
public Technology2()
super();
public Technology2(String id, String name, int experience, boolean certified)
super();
this.id = id;
this.name = name;
this.experience = experience;
this.certified = certified;
/**
* @return the id
*/
public String getId()
return id;
/**
* @param id the id to set
*/
@Field(value="id")
public void setId(String id)
this.id = id;
/**
* @return the name
*/
public String getName()
return name;
/**
* @param name the name to set
*/
@Field(value="name")
public void setName(String name)
this.name = name;
/**
* @return the experience
*/
public int getExperience()
return experience;
/**
* @param experience the experience to set
*/
@Field(value="experience_i")
public void setExperience(int experience)
this.experience = experience;
/**
* @return the certified
*/
public boolean getCertified()
return certified;
/**
* @param certified the certified to set
*/
@Field(value="certified_b")
public void setCertified(boolean certified)
this.certified = certified;
/**
* @return the content_type
*/
public String getContent_type()
return content_type;
/**
* @param content_type the content_type to set
*/
@Field(value="content_type_t")
public void setContent_type(String content_type)
this.content_type = content_type;
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
return "Technology2 [id=" + id + ", name=" + name + ", experience=" + experience + ", certified=" + certified +
", content_type=" + content_type + "]";
如果员工 bean 具有嵌套的地址 Bean,则 Bean 插入方法可以正常工作,但是在我们的示例中,员工 bean 具有嵌套的技术 Bean 集合,它会导致以下行异常
UpdateResponse 响应 = solrClient.addBean(bean);
插入方式:
public <T> boolean insert (T bean)
try
UpdateResponse response = solrClient.addBean(bean);
System.out.println("insert bean ElapsedTime: " + response.getElapsedTime());
solrClient.commit();
return true;
catch (IOException | SolrServerException e)
e.printStackTrace();
return false;
这里是返回空指针异常,下面是Employee2的toString值
Employee2 [id=EE130S, name=Vulrp, designation=NjLtK, 工资=127334.59626719051,总经验=49.989444163266164, 技术2=[技术2 [id=0TE130S,名称=uyIOFlh,经验=21, 认证=真,内容类型=技术2]技术2 [id=1TE130S, 名称=FmZJjak,经验=43,认证=假, content_type=technology2]Technology2 [id=2TE130S, name=ddJbOXg, 经验=11,认证=假,内容类型=技术2]技术2 [id=3TE130S,名称=rIxumUe,经验=5,认证=真, content_type=technology2]], content_type=employee2]
导致以下异常:
java.lang.NullPointerException
at org.apache.solr.client.solrj.beans.DocumentObjectBinder$DocField.storeType(DocumentObjectBinder.java:243)
at org.apache.solr.client.solrj.beans.DocumentObjectBinder$DocField.<init>(DocumentObjectBinder.java:183)
at org.apache.solr.client.solrj.beans.DocumentObjectBinder.collectInfo(DocumentObjectBinder.java:144)
at org.apache.solr.client.solrj.beans.DocumentObjectBinder.getDocFields(DocumentObjectBinder.java:123)
at org.apache.solr.client.solrj.beans.DocumentObjectBinder.toSolrInputDocument(DocumentObjectBinder.java:76)
at org.apache.solr.client.solrj.SolrClient.addBean(SolrClient.java:277)
at org.apache.solr.client.solrj.SolrClient.addBean(SolrClient.java:259)
at com.opteamix.buildpal.poc.SampleSolrDAO.insert(SampleSolrDAO.java:62)
at com.opteamix.buildpal.poc.SampleSolrDAOTest.testEmployees2Insert(SampleSolrDAOTest.java:94)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at junit.framework.TestCase.runTest(TestCase.java:168)
at junit.framework.TestCase.runBare(TestCase.java:134)
at junit.framework.TestResult$1.protect(TestResult.java:110)
at junit.framework.TestResult.runProtected(TestResult.java:128)
at junit.framework.TestResult.run(TestResult.java:113)
at junit.framework.TestCase.run(TestCase.java:124)
at junit.framework.TestSuite.runTest(TestSuite.java:243)
at junit.framework.TestSuite.run(TestSuite.java:238)
at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:83)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
【问题讨论】:
【参考方案1】:插入关联 bean 列表的 bean 对象现在按预期工作。
终于在看了solrj6.0.0源码之后,找到了解决方法。实际上 solrj6.0.0 中存在一个错误。那是: 如果我们在 Employee2 bean 的 set 方法中给出 @Field 注释,如下所示:
/**
* @param technologies2 the technologies2 to set
*/
@Field (child = true)
public void setTechnologies2(Collection<Technology2> technologies2)
this.technologies2 = technologies2;
然后它导致我们的 Employee2 bean 插入异常,它聚合了技术列表。这似乎是来自 sorlj 代码的错误:
DocumentObjectBinder 的嵌套 DocField 类有以下实现:
public DocField(AccessibleObject member)
if (member instanceof java.lang.reflect.Field)
field = (java.lang.reflect.Field) member;
else
setter = (Method) member;
annotation = member.getAnnotation(Field.class);
storeName(annotation);
storeType();
// Look for a matching getter
if (setter != null)
String gname = setter.getName();
if (gname.startsWith("set"))
gname = "get" + gname.substring(3);
try
getter = setter.getDeclaringClass().getMethod(gname, (Class[]) null);
catch (Exception ex)
// no getter -- don't worry about it...
if (type == Boolean.class)
gname = "is" + setter.getName().substring(3);
try
getter = setter.getDeclaringClass().getMethod(gname, (Class[]) null);
catch(Exception ex2)
// no getter -- don't worry about it...
由于我们在 setter 中注释了 @Field (child = true),因此在这种情况下,字段为 null,这导致 storeType() 方法导致空指针异常
private void storeType()
if (field != null)
type = field.getType();
else
Class[] params = setter.getParameterTypes();
if (params.length != 1)
throw new BindingException("Invalid setter method. Must have one and only one parameter");
type = params[0];
if (type == Collection.class || type == List.class || type == ArrayList.class)
isList = true;
if (annotation.child())
populateChild(field.getGenericType());
else
type = Object.class;
else if (type == byte[].class)
//no op
else if (type.isArray())
isArray = true;
if (annotation.child())
populateChild(type.getComponentType());
else
type = type.getComponentType();
else if (type == Map.class || type == HashMap.class) //corresponding to the support for dynamicFields
if (annotation.child()) throw new BindingException("Map should is not a valid type for a child document");
isContainedInMap = true;
//assigned a default type
type = Object.class;
if (field != null)
if (field.getGenericType() instanceof ParameterizedType)
//check what are the generic values
ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
Type[] types = parameterizedType.getActualTypeArguments();
if (types != null && types.length == 2 && types[0] == String.class)
//the key should always be String
//Raw and primitive types
if (types[1] instanceof Class)
//the value could be multivalued then it is a List, Collection, ArrayList
if (types[1] == Collection.class || types[1] == List.class || types[1] == ArrayList.class)
type = Object.class;
isList = true;
else
//else assume it is a primitive and put in the source type itself
type = (Class) types[1];
else if (types[1] instanceof ParameterizedType) //Of all the Parameterized types, only List is supported
Type rawType = ((ParameterizedType) types[1]).getRawType();
if (rawType == Collection.class || rawType == List.class || rawType == ArrayList.class)
type = Object.class;
isList = true;
else if (types[1] instanceof GenericArrayType) //Array types
type = (Class) ((GenericArrayType) types[1]).getGenericComponentType();
isArray = true;
else //Throw an Exception if types are not known
throw new BindingException("Allowed type for values of mapping a dynamicField are : " +
"Object, Object[] and List");
else
if (annotation.child())
populateChild(type);
所以到目前为止,我在字段级别而不是在 setter 注释 @Field 注释:
@Field (child = true)
private Collection<Technology2> technologies2;
所以现在插入这样的 bean 是成功的,在检索我得到低于预期的结果:
Employee2 [id=E3, name=KzWhg, designation=aTDiu, 工资=190374.70126209356,总经验=2.0293696897450584, 技术2 = [技术2 [id = 0T3,名称= nxTdufv,经验= 46, 认证=false, content_type=technology2]Technology2 [id=1T3, 名称=waSMXpf,经验=26,认证=假, content_type=technology2]Technology2 [id=2T3, name=jqNbZZr, 经验=30,认证=真,内容类型=技术2]技术2 [id=3T3,姓名=VnidjyI,经验=21,认证=真, content_type=technology2]Technology2 [id=4T3, name=ulGnHFm, 经验=33,认证=假,内容类型=技术2]技术2 [id=5T3,名称=cpUfgrY,经验=21,认证=假, content_type=technology2]], content_type=employee2] Employee2 [id=E4, 姓名=xeKOY,职务=WfPSm,工资=169700.53869292728, 总经验=22.047282596410284,技术2=[技术2 [id=0T4, name=rleygcW, experience=30, Certified=true, content_type=technology2]Technology2 [id=1T4, name=yxjHrxV, 经验=27,认证=假,内容类型=技术2]技术2 [id=2T4,名称=czjHAEE,经验=31,认证=假, content_type=technology2]Technology2 [id=3T4, name=RDhoIJw, 经验=22,认证=假,内容类型=技术2]技术2 [id=4T4,名称=UkbldDN,经验=19,认证=假, content_type=technology2]], content_type=employee2] Employee2 [id=E5, 姓名=tIWuY,职务=WikuL,薪水=41462.47225086359, 总经验=13.407976384902403,技术2=[技术2 [id=0T5,名称=CDCMunq,经验=6,认证=假, content_type=technology2]Technology2 [id=1T5, name=NmkADyB, 经验=31,认证=假,内容类型=技术2]技术2 [id=2T5,名称=IhXnLfc,经验=9,认证=真, content_type=technology2]], content_type=employee2] Employee2 [id=E6, 姓名=YluDp,职务=EtFqG,工资=159724.66206009954, 总经验=26.26819742766281,技术2=[技术2 [id=0T6, 名称=mFvKDIK,经验=33,认证=假, content_type=technology2]Technology2 [id=1T6, name=arTNoHj, 经验=44,认证=真,内容类型=技术2]技术2 [id=2T6,名称=KYMseTW,经验=34,认证=假, content_type=technology2]Technology2 [id=3T6, name=ZTphSVn, 经验=13,认证=真,内容类型=技术2]], content_type=employee2] Employee2 [id=E7, name=qMkKG, 职称=SQHCo,工资=111861.53447042785, 总经验=13.29234679211927,技术2=[技术2 [id=0T7, 名称=PTKxjFl,经验=23,认证=假, content_type=technology2]Technology2 [id=1T7, name=gJfxbto, 经验=17,认证=真,内容类型=技术2]技术2 [id=2T7, name=eekPYPN, experience=40, Certified=true, content_type=technology2]Technology2 [id=3T7, name=aRdsEag, 经验=40,认证=真,内容类型=技术2]技术2 [id=4T7,名称=loDFVyM,经验=40,认证=真, content_type=technology2]Technology2 [id=5T7, name=xPXNaDV, 经验=0,认证=假,内容类型=技术2]], content_type=employee2] Employee2 [id=E8, name=WyNsf, 职称=TtanH,工资=107942.13641940584, 总经验=47.036469485140984,技术2=[技术2 [id=0T8,名称=kakGXqh,经验=14,认证=真, content_type=technology2]Technology2 [id=1T8, name=ugwgdHy, 经验=9,认证=真,内容类型=技术2]技术2 [id=2T8,名称=rNzwcdQ,经验=31,认证=假, content_type=technology2]Technology2 [id=3T8, name=ZBXUhuB, 经验=6,认证=真,内容类型=技术2]], content_type=employee2] Employee2 [id=E9, name=EzuLC, 职称=IXYGj,工资=133064.4485190016, 总经验=16.075378097234232,技术2=[技术2 [id=0T9,名称=GmvOUWp,经验=5,认证=真, content_type=technology2]Technology2 [id=1T9, name=ZWyvRxk, 经验=24,认证=假,内容类型=技术2]技术2 [id=2T9,名称=uWkTrfB,经验=5,认证=假, content_type=technology2]Technology2 [id=3T9, name=NFknqJj, 经验=29,认证=真,内容类型=技术2]], content_type=employee2] Employee2 [id=E10, name=quFKB, 职称=eUoBJ,薪水=198332.3270496455, 总经验=14.035578311712438,技术2=[技术2 [id=0T10,名称=MOXduwi,经验=49,认证=假, content_type=technology2]Technology2 [id=1T10, name=LpXGRvn, 经验=28,认证=假,内容类型=技术2]技术2 [id=2T10,名称=QeAOjIp,经验=3,认证=真, content_type=technology2]Technology2 [id=3T10, name=aVxGhOV, 经验=34,认证=真,内容类型=技术2]技术2 [id=4T10,名称=fbSaBUm,经验=42,认证=真, content_type=technology2]], content_type=employee2]
我在 JIRA 中提出了代码缺陷: https://issues.apache.org/jira/browse/SOLR-9112
【讨论】:
【参考方案2】:即使 Solrj 代码也不支持将多个子项作为 bean 插入。它给出了例外:不支持多个孩子。但是通过其他方式,可以通过 SolrInput 类插入/索引聚合多个 bean 的 Bean。
【讨论】:
是的,为此我将敲响该错误。那么根据 SolrJ 代码,如果有超过 1 个 Child,那么 solrClient.addBean(bean);通过手动异常的方法:不支持超过 1 个孩子。但是通过 SolrInputDocument,我们可以添加多个孩子。我正在考虑编写一个反射代码,它将支持插入关联多个 bean 的 bean,以便反射代码处理此 url 中提到的代码类型:[github.com/lucidworks/solrj-nested-docs/blob/master/… ***.com/questions/37677538/…以上是关于SOLRJ-6.0.0:插入关联 bean 对象列表的 bean 对象给出空指针异常的主要内容,如果未能解决你的问题,请参考以下文章
(mybatis)直接执行sql结果为空,但是session.selectList出的list结果中包含了一个属性为null的bean对象