JPA, How to find an object that has composite id? - jpa

Based on second approach answered here I designed my JPA class.
#Entity(name = "SearchKeywordJPA")
#IdClass(SearchKeywordJPA.SearchKeyId.class)
public class SearchKeywordJPA implements Comparable<SearchKeywordJPA> {
#Id
private String keyword;
#Id
private long date;
private String userUUID;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SearchKeywordJPA that = (SearchKeywordJPA) o;
if (date != that.date) return false;
if (!keyword.equals(that.keyword)) return false;
if (!userUUID.equals(that.userUUID)) return false;
return true;
}
#Override
public int hashCode() {
int result = keyword.hashCode();
result = 31 * result + (int) (date ^ (date >>> 32));
result = 31 * result + userUUID.hashCode();
return result;
}
#Override
public String toString() {
return "SearchKeywordJPA{" +
"keyword='" + keyword + '\'' +
", date=" + date +
", userUUID='" + userUUID + '\'' +
'}';
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public long getDate() {
return date;
}
public void setDate(long date) {
this.date = date;
}
public String getUserUUID() {
return userUUID;
}
public void setUserUUID(String userUUID) {
this.userUUID = userUUID;
}
#Override
public int compareTo(SearchKeywordJPA searchRecord) {
long comparedDate = searchRecord.date;
if (this.date > comparedDate) {
return 1;
} else if (this.date == comparedDate) {
return 0;
} else {
return -1;
}
}
/**********************
* Key class
**********************/
public class SearchKeyId {
private int id;
private int version;
}
}
In my servlet I want to check datastore and store my object if is not exist.
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
...
for(SearchKeywordJPA item: applicationList) {
if(!isRecorded(item))
storeRecord(item);
}
}
private boolean isRecorded(SearchKeywordJPA record) {
EntityManager em = EMF.get().createEntityManager();
SearchKeywordJPA item = em.find(SearchKeywordJPA.class, record);
return item != null;
}
private void storeRecord(SearchKeywordJPA record) {
EntityManager em = EMF.get().createEntityManager();
em.persist(record);
}
However when I run, application crashes and log says
javax.persistence.PersistenceException: org.datanucleus.store.appengine.FatalNucleusUserException: Received a request to find an object of type com.twitterjaya.model.SearchKeywordJPA identified by SearchKeywordJPA{keyword='airasia', date=1335680686149, userUUID='FFFF0000'}. This is not a valid representation of a primary key for an instance of com.twitterjaya.model.SearchKeywordJPA.
What is the reason? any suggestion would be appreciated. Thanks

You pass an instance of the IdClass into em.find ... i.e SearchKeyId. Obviously if you really have an IdClass that has no equals/hashCode/toString/constructor then you will likely get many problems. Those problems will only be increased by using an ancient plugin for GAE/Datastore.

If your Key is
#Entity(name = "SearchKeywordJPA")
#IdClass(SearchKeywordJPA.SearchKeyId.class)
public class SearchKeywordJPA implements Comparable<SearchKeywordJPA> {
you are doing it wrong.
IdClass does not need any annotation of #IdClass just the #Id
annotation.
Key can not be an entity.
Need to implements Serializable , comparable is not needed
Need to override equals and hascode and have no arg constructor
Class key need to be as follows.
public class SearchKeyId implements Serializable {
private String keyword;
private long date;
And your entity I assume something like this.
#Entity(name = "SearchKeywordJPA")
#IdClass(SearchKeyId.class)
public class SearchKeywordJPA {
#Id
private String keyword;
#Id
private long date;
private String userUUID;
Just consider that find method will use the SearchKey.class to find
the entities.
Fields that are in the IdClass need to have #Id annotation in the entity.
Key can not be an entity on its own.
Comparable is not really needed as all the comparison are placed in the IdClass

Related

Is Realm's .where faster than filtering a list manually?

I am creating a project using a realm database.
I also have a lot of lists which I wish to filter while the user types.
The biggest one of them will probably be the list of customers
Currently I am using realm by using a wrapper class for each object (if anyone has a better suggestion I'm definitely open to it)
The objects I am using are like this:
public class Customer_Realm extends RealmObject{
private int _id;
private String _name; //...etc
public int get_id(){ return _id; }
public void set_id(int id) { _id = id;}
//etc
}
Public class Customer() {
Customer_Realm _base;
public Customer(Customer_Realm base) { _base = base;}
public int get_id(){ return _base.get_id(); }
public void set_id(int id) { _base.set_id(id);}
//...etc
}
Along with this I have an ArrayListAdapter for customer objects. My question is, would this:
public class CustomerAdapter extends ArrayListAdapter<Customer> {
List<Customer> _customers;
String _filter = null;
public void set_filter(string name) {
_filter = name;
notifyDataSetChanged();
}
#override
public int getCount(){
return get_visible().count();
}
public Customer getItem(int position){
return get_visible().get(position);
}
public List<Customer> get_visible() {
if (_filter == null)
return _customers;
List<Customer> visible = new ArrayList<Customer> ();
foreach (Customer c : _customers){
if (c.get_name().contains(_filter)
visible.add(c);
}
return visible;
}
}
Or would this be better?
public class CustomerAdapter extends ArrayListAdapter<Customer> {
Realm _database;
String _filter = null;
public void set_filter(string name) {
_filter = name;
notifyDataSetChanged();
}
#override
public int getCount(){
return get_visible().count();
}
public Customer getItem(int position){
return get_visible().get(position);
}
public List<Customer> get_visible() {
RealmResults<Customer_Realm> result = null;
if (_filter == null)
result = _database.allObjects(Customer_Realm.class);
else
result = _database.where(Customer_realm.class).contains("_name",_filter);
List<Customer> visible = new ArrayList<Customer> ();
foreach (Customer_Realm c : result){
visible.add(new Customer(c));
}
return visible;
}
}
Unfortunately my database contains only a small sample of data(and realm for windows does not have an editor so I can enter data manually) so when I try either implementation I do not see any difference, I was wondering if anyone has any experience with what happens when you use such an implementation with a large number of data
Thanks in advance for any help you can provide

JPA2.0 property access in spring rest data -- some getters not being called

I am still somewhat of a novice with Spring Boot and Spring Data Rest and hope someone out there with experience in Accessing by Property. Since I cannot change a database which stores types for Letters in an unnormalized fashion (delimited string in a varchar), I thought that I could leverage some logic in properties to overcome this. However I notice that when using property access, some of my getters are never called.
My Model code:
package ...
import ...
#Entity
#Table(name="letters", catalog="clovisdb")
#Access(AccessType.PROPERTY)
public class Letter {
public enum PhoneticType {
VOWEL, SHORT, LONG, COMMON;
public static boolean contains(String s) { ... }
}
public enum PositionType {
ALL, INITIAL, MEDIAL, FINAL;
public static boolean contains(String s) { ... }
}
public enum CaseType {
ALL, LOWER, UPPER;
public static boolean contains(String s) { ... }
}
private int id;
private String name;
private String translit;
private String present;
private List<PhoneticType> phoneticTypes;
private CaseType caseType;
private PositionType positionType;
#Id
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getTranslit() { return translit; }
public void setTranslit(String translit) { this.translit = translit; }
public String getPresent() { return present; }
public void setPresent(String present) { this.present = present; }
public String getTypes() {
StringBuilder sb = new StringBuilder(); //
if (phoneticTypes!=null) for (PhoneticType type : phoneticTypes) sb.append(" ").append(type.name());
if (caseType!=null) sb.append(" ").append(caseType.name());
if (positionType!=null) sb.append(" ").append(positionType.name());
return sb.substring( sb.length()>0?1:0 );
}
public void setTypes(String types) {
List<PhoneticType> phoneticTypes = new ArrayList<PhoneticType>();
CaseType caseType = null;
PositionType positionType = null;
for (String val : Arrays.asList(types.split(" "))) {
String canonicalVal = val.toUpperCase();
if (PhoneticType.contains(canonicalVal)) phoneticTypes.add(PhoneticType.valueOf(canonicalVal));
else if (CaseType.contains(canonicalVal)) caseType = CaseType.valueOf(canonicalVal);
else if (PositionType.contains(canonicalVal)) positionType = PositionType.valueOf(canonicalVal);
}
this.phoneticTypes = phoneticTypes;
this.caseType = (caseType==null)? CaseType.ALL : caseType;
this.positionType = (positionType==null)? PositionType.ALL : positionType;
}
#Override
public String toString() { .... }
}
My Repository/DAO code:
package ...
import ...
#RepositoryRestResource
public interface LetterRepository extends CrudRepository<Letter, Integer> {
List<Letter> findByTypesLike(#Param("types") String types);
}
Hitting this URI: http://mytestserver.com:8080/greekLetters/6
and setting breakpoints on all the getters and setters, I can see that the properties are called in this order:
setId
setName
setPresent
setTranslit
setTypes
(getId not called)
getName
getTranslit
getPresent
(getTypes not called !!)
The json returned for the URI above reflects all the getters called, and there are no errors
{
"name" : "alpha",
"translit" : "`A/",
"present" : "Ἄ",
"_links" : {
"self" : {
"href" : "http://mytestserver.com:8080/letters/6"
}
}
}
But why is my getTypes() not being called and my JSON object missing the “types” attribute? I note that the setter is called, which makes it even stranger to me.
Any help would be appreciated!
Thanks in advance
That's probably because you don't have a field types, so getTypes() isn't a proper getter. Try adding this to your entity
#Transient
private String types;
I don't know how the inner works, but it's possible that the class is first scanned for its fields, and then a getter is called for each field. And since you don't have types field, the getter isn't called. Setter getting called could be a feature but I wouldn't be surprised if it is a bug, because findByTypesLike should translate to find Letters whose types field is like <parameter>, and types is not a field.
Another thing you can try, is to annotate that getter with #JsonInclude. Jackson 2 annotations are supported in Spring versions 3.2+ (also backported to 3.1.2).

JPA Eclipselink JOIN FETCH LAZY relation returning null

I am always getting NULL from a JOIN FETCH clause in my JPA Query, even though I have everything configured as expected:
#XmlRootElement
#XmlAccessorType(XmlAccessType.PROPERTY)
#Entity
#Table(name = "TB_BANNER_IMAGE")
public class BannerImage extends BaseEntity<Integer> {
protected FileReference fileReference;
private String type;
private String labelTitle;
protected BannerImage() {}
#Id
#TableGenerator(name="genBannerImage", table="TB_ID_GENERATOR",
pkColumnName="ID_NAME", valueColumnName="ID_VAL",
pkColumnValue="TB_BANNER_IMAGE", allocationSize=1)
#GeneratedValue(strategy=GenerationType.TABLE, generator="genBannerImage")
#Column(name = "ID_BANNER_IMAGE", unique = true, nullable = false)
public Integer getId() {
return super.getId();
}
#Override
public void setId(Integer id) {
super.setId(id);
}
#Column(name="TYPE")
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#OneToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
#JoinColumn(name="ID_FILE_REFERENCE", nullable=false)
public FileReference getFileReference() {
return fileReference;
}
public void setFileReference(FileReference fileReference) {
this.fileReference = fileReference;
}
#Column(name="LABEL_TITLE")
public String getLabelTitle() {
return labelTitle;
}
public void setLabelTitle(String labelTitle) {
this.labelTitle = labelTitle;
}
}
for File Reference Class:
#Entity
#Table(name = "TB_FILE_REFERENCE")
public class FileReference extends BaseNamedEntity<String> {
private String type;
public FileReference() {}
#Id
#TableGenerator(name="genFileReference", table="TB_ID_GENERATOR",
pkColumnName="ID_NAME", valueColumnName="ID_VAL",
pkColumnValue="TB_FILE_REFERENCE", allocationSize=1)
#GeneratedValue(strategy=GenerationType.TABLE, generator="genFileReference")
#Column(name = "ID_FILE_REFERENCE", unique = true, nullable = false)
public String getId() {
return super.getId();
}
#Override
public void setId(String id) {
super.setId(id);
}
#Column(name = "TYPE")
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
Service class:
#Path("/banner")
public class BannerImageService extends BaseServiceFacade<BannerImage, Integer> {
#SuppressWarnings("unchecked")
#Override
public Crud<BannerImage, Integer> lookupService() throws ServiceLocatorException {
return ServiceLocator.getInstance()
.getLocalHome(ServicesConstants.BANNER_IMAGE_SERVICE);
}
#Override
protected String getDefaultGetQuery() {
return BannerImageDAO.GET_BY_ID_FETCH_FILE_REF;
}
#Override
protected String getDefaultQuery() {
return BannerImageDAO.GET_ALL_FETCH_FILE_REF;
}
}
get REST method of BaseServiceFacade:
#Override
#GET
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/{id}")
public T get(#PathParam("id") ID id) {
try {
if (!validateID(id)) {
logMessage("Invalid Entity ID: " + id);
return null;
}
String defaultGetQuery = getDefaultGetQuery();
if (defaultGetQuery != null) {
Map<String, Object> mapParams = new HashMap<String, Object>();
mapParams.put("id", id);
List<T> entityList = getService().search(defaultGetQuery, mapParams);
if (entityList != null && entityList.size() == 1) {
T ent = entityList.get(0);
return ent;
} else {
logMessage("Invalid search by Entity ID: " + id);
}
} else {
return getService().findById(clazz, id);
}
} catch (ServiceException e) {
serviceException(e);
} catch (Exception ex) {
logException(ex);
}
return null;
}
And finally the Service Bean EJB which reads from entityManager:
public class BaseServiceBean<T extends IEntity<ID>, ID extends Serializable> implements Crud<T,ID> {
// ... generic methods to be reused by subclasses
#Override
public List<T> search(String queryOrNamedQuery) throws ServiceException {
return search(queryOrNamedQuery, null, 0, 0);
}
#SuppressWarnings("unchecked")
public List<T> search(String namedQueryOrHql, Map<String, Object> parameters, int start, int chunkSize) {
try {
Query query = createQuery(namedQueryOrHql, getQueryType(namedQueryOrHql));
if (start > 0) {
query.setFirstResult(start);
}
if (chunkSize > 0) {
query.setMaxResults(chunkSize);
}
addParameters(query, parameters);
List<T> result = query.getResultList();
afterSearch(result);
return result;
} catch (NoResultException nre) {
nre.printStackTrace();
} catch (ClassCastException cce) {
cce.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
protected void afterSearch(List<T> result) {
}
// etc...
implementation specific class for BannerImageService:
#Stateless(mappedName="ejb/BannerImageService")
public class BannerImageServiceBean extends BaseServiceBean<BannerImage, Integer> implements BannerImageServiceBeanRemote, BannerImageServiceBeanLocal {
#Override
protected void afterSearch(List<BannerImage> result) {
if (result != null && result.size() == 1) {
BannerImage bannerImage = result.get(0);
bannerImage.getFileReference();
}
super.afterSearch(result);
}
// additional code ...
When I try to fetch my BannerImage class together with it's corresponding FileReference member I always get NULL even though in my DB there is an existing foreign key present:
JPQL:
"SELECT a FROM BannerImage a join fetch a.fileReference WHERE a.id = :id";
Generated SQL:
SELECT t1.ID_BANNER_IMAGE, t1.LABEL_TEXT, t1.LABEL_TITLE, t1.TYPE,
t1.ID_FILE_REFERENCE, t0.ID_FILE_REFERENCE, t0.NAME,
t0.TYPE FROM TB_FILE_REFERENCE t0, TB_BANNER_IMAGE
t1 WHERE (t0.ID_FILE_REFERENCE = t1.ID_FILE_REFERENCE) AND t1.ID_BANNER_IMAGE = 1
in my DB the record shows a correct reference:
BANNER_IMAGE:
1;"";"main";"2bdbb063d0d0ee2939c89763945d9d9e";"banner1.png";"image/png"
If I execute :
select * from TB_FILE_REFERENCE where ID_FILE_REFERENCE = '2bdbb063d0d0ee2939c89763945d9d9e'
I can find the record in the DB, although my EclipseLink JPA Implementation always returns null:
EclipseLink Version 2.5.2-M1
This is how the Entity gets passed from Service Layer to the
Can someone help pointing why the JOIN FETCH is not properly working?
I faced a similar issue and looking closely I see that this issue was happening only to entities recently created/saved. Then I figured that it has something to do with eclipselink cache. I solved this problem by adding this line before making a join fetch JPQL query,
em.getEntityManagerFactory().getCache().evictAll();
em.createQuery("SELECT a FROM BannerImage a join fetch a.fileReference WHERE a.id = :id").getResultList();
HTH!

JSF2.0, JPA: How to set list element (mapped in #OneToMany)

I'm looking for good practices and clean solution to my problem.
There are 2 entity classes:
#Entity
public class Site implements Serializable {
...
#OneToMany(mappedBy = "site")
private List<SiteIp> siteIpList;
...
public List<SiteIp> getSiteIpList() {
return siteIpList;
}
public void setSiteIpList(List<SiteIp> siteIpList) {
this.siteIpList = siteIpList;
}
}
#Entity
#IdClass(SiteIpPK.class)
public class SiteIp implements Serializable {
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Site site;
#Id
private int idx;
private String ip;
/* other stuff such as constructors, getters and setters */
}
SiteIpPK has 2 columns, it should be clean :)
Obviously, this is commonly used model in world.
Next, there is a view layer. JSF page has 3 fields showing SiteIp.ip for given site and idx. As far, I've wrote helper getter method on Site entity:
public String getIpForIdx(Integer idx) {
// there's no simple siteIpList.get(idx), because of additional logic in getter
// so let's iterate through entire list
for (SiteIp siteIp : this.siteIpList) {
if (/* other logic returns true value && */ siteIp.getIdx() == idx) {
return siteIp.getIp();
}
}
return null;
}
JSF expression language is constructed as follows:
<h:inputText id="sync1_ip" value="#{siteController.editContext.site.getIpForIdx(1)}" />
Now, when page is accessed, proper IP values is propagated to input text field, as far it's all good. But, when IP changed and form is submitted, EL throws exception:
javax.el.PropertyNotWritableException: /edit/siteEdit.xhtml #47,123 value="#{siteController.editContext.site.getIpForIdx(1)}": Illegal Syntax for Set Operation
I understand and acknowledge that, so there is my question:
What are best practices to handle this issue? Somewhat awful solution is to write as many helper methods as unique indexes exists:
public String getIp1() {
return this.IpForIdx(1);
}
public String getIp2() {
return this.IpForIdx(2);
}
/* ... */
public void setIp1(String newIp) {
this.siteIpList.add(1, newIp);
}
public void setIp2(String newIp) {
this.siteIpList.add(2, newIp);
}
/* ... and so on... */
with JSF EL:
<h:inputText id="sync1_ip" value="#{siteController.editContext.site.ip1}" />
Are there other, more flexible and beautiful solutions?
You should better google for 'composite primary key with JPA'. But as you're new here, below goes a simple explanation and a working solution.
Small explanation
Composite primary keys are themselves classes with #Embeddable annotation. And they become a primary key in your #Entity class as soon as you mark your primary composite key field with #EmbeddedId. The only thing left is to implement equals and hashcode appropriately in both classes.
Solution
The solution consists of an #Entity class
#Entity
#Table(name="...")
#AssociationOverride(name = "pk.site", joinColumns = #JoinColumn(name = "..."))
public class SiteIp implements Serializable {
private SiteIpPk pk = new SiteIpPK();
private String ip;
public SiteIp() { }
#EmbeddedId
public SiteIpPK getPk() {
return pk;
}
public void setPk(SiteIpPk pk) {
this.pk = pk;
}
#Transient
public Site getSite() {
return pk.getSite();
}
public void setSite(Site site) {
pk.setSite(site);
}
#Transient
public Integer getIdx() {
return pk.getIdx();
}
public void setIdx(Integer idx) {
pk.setIdx(idx);
}
#Column(name="...")
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
SiteIp that = (SiteIp) o;
if (pk != null ? !pk.equals(that.pk) : that.pk != null)
return false;
return true;
}
public int hashCode() {
return (pk != null ? pk.hashCode() : 0);
}
}
and #Embeddable class
#Embeddable
public class SiteIpPk implements Serializable {
private Site site;
private Integer idx;
#ManyToOne
public Site getSite() {
return site();
}
public void setSite(Site site) {
this.site = site;
}
#Column(name="...", nullable = false)
public Integer getIdx() {
return idx;
}
public void setIdx(Integer idx) {
this.idx = idx;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
SiteIpPk that = (SiteIpPk) o;
if (site != null ? !site.equals(that.site) : that.site != null)
return false;
if (idx != null ? !idx.equals(that.idx) : that.idx != null)
return false;
return true;
}
public int hashCode() {
int result;
result = (site != null ? site.hashCode() : 0);
result = 17 * result + (idx != null ? idx.hashCode() : 0);
return result;
}
}
In the end, learn more yourself and check if the problem has already been investigated!

Guava Group By Key

Still learning all the features of the Guava API. Wanted to shoot this out there to see if there were any suggestions on how to go about doing this. The current picture looks like this: List < ObjectA >, List< ObjectB >, List< ObjectC >
ObjectA {
Integer attribute1;
String attribute2;
….
}
ObjectB {
Integer attribute1;
String attribute2;
….
}
ObjectC {
Integer attribute1;
String attribute2;
….
}
I would like to take the 3 lists and group the objects together by attribute1 and attribute2 thus creating something like this:
123|”Whatever” : { Object A, ObjectB, ObjectC}
456|”Something” : { Object A, ObjectB, ObjectC}
An obvious *Multimap is the return but the process of building this could have multiple solutions. Thoughts on how to approach this?
I would use Multimaps.index() with a custom pair class to represent the combination of attribute1 and attribute2.
/**
* A "pair" class that contains an {#code attribute1} and an {#code attribute2}. Mainly used as a {#link Map} key.
*/
#Immutable
public class Attribute1AndAttribute2 {
#Nullable
private final Integer attribute1;
#Nullable
private final String attribute2;
public Attribute1AndAttribute2(#Nullable Integer attribute1,
#Nullable String attribute2) {
this.attribute1 = attribute1;
this.attribute2 = attribute2;
}
#Nullable
public Integer getAttribute1() {
return attribute1;
}
#Nullable
public String getAttribute2() {
return attribute2;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Attribute1AndAttribute2) {
Attribute1AndAttribute2 that = (Attribute1AndAttribute2) obj;
return Objects.equal(this.attribute1, that.attribute1)
&& Objects.equal(this.attribute2, that.attribute2);
}
return false;
}
#Override
public int hashCode() {
return Objects.hashCode(attribute1, attribute2);
}
}
/**
* Static utility methods pertaining to {#link ObjectA}, {#link ObjectB}, and {#link ObjectC}'s {#code attribute1} and
* {#code attribute2}.
*/
public final class Attribute1AndAttribute2Utils {
private Attribute1AndAttribute2Utils() { /* prevents instantiation */ }
public static Multimap<Attribute1AndAttribute2, Object> groupedByAttribute1AndAttribute2(List<ObjectA> as, List<ObjectB> bs, List<ObjectC> cs) {
Iterable<Object> abcs = Iterables.concat(as, bs, cs);
return Multimaps.index(abcs, Attribute1AndAttribute2ForObjectFunction.INSTANCE);
}
// enum singleton pattern
private enum Attribute1AndAttribute2ForObjectFunction implements Function<Object, Attribute1AndAttribute2> {
INSTANCE;
#Override
public Attribute1AndAttribute2 apply(#Nullable Object object) {
if (object instanceof ObjectA) {
ObjectA objectA = (ObjectA) object;
return new Attribute1AndAttribute2(objectA.attribute1, objectA.attribute2);
} else if (object instanceof ObjectB) {
ObjectB objectB = (ObjectB) object;
return new Attribute1AndAttribute2(objectB.attribute1, objectB.attribute2);
} else if (object instanceof ObjectC) {
ObjectC objectC = (ObjectC) object;
return new Attribute1AndAttribute2(objectC.attribute1, objectC.attribute2);
} else {
throw new RuntimeException("Object must be ObjectA, ObjectB, or ObjectC, but was " + object);
}
}
}
}
Note that the code would a be a lot cleaner / better if you had an interface such as:
public interface HasAttribute1AndAttribute2 {
Integer getAttribute1();
String getAttribute2();
}
Also, I assumed that attribute1 and attribute2 may be null. If they are never null, consider changing the pair class to:
/**
* A "pair" class that contains an {#code attribute1} and an {#code attribute2}. Mainly used as a {#link Map} key.
*/
#Immutable
public class Attribute1AndAttribute2 {
#Nonnull
private final Integer attribute1;
#Nonnull
private final String attribute2;
public Attribute1AndAttribute2(#Nonnull Integer attribute1, #Nonnull String attribute2) {
this.attribute1 = checkNotNull(attribute1);
this.attribute2 = checkNotNull(attribute2);
}
#Nonnull
public Integer getAttribute1() {
return attribute1;
}
#Nonnull
public String getAttribute2() {
return attribute2;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Attribute1AndAttribute2) {
Attribute1AndAttribute2 that = (Attribute1AndAttribute2) obj;
return this.attribute1.equals(that.attribute1)
&& this.attribute2.equals(that.attribute2);
}
return false;
}
#Override
public int hashCode() {
return Objects.hashCode(attribute1, attribute2);
}
}