0

I have a POJO that maps to a row in a specific table. The row describes an image in some site and contains data like width, height, url, some sort of status and some more fields. In some legacy code I have a query (in hibernate) that returns the url and the status. This data is encapsulated in a class ImageStatusAndOrigFilename.

I think this is a bad idea because:

  1. What if tomorrow I need to query for some other fields? The name is too coupled to the data.
  2. In the past the only way to get the image width and height was to parse the url. Today we map the width and height in the db and thus I now need to get the image size and status (and I don't care anymore about the original file name). So I need to change this class but can't because it's being used in other places in the code. I wish to get to something more generic that is not coupled to a specific scenario and can be extended when needed.

I'm trying to figure out which data structure to use. Should I use the original POJO that has all the fields but leave some of them null (I don't want to query all of the fields as I don't need all of them in this scenario). Should I create another POJO for this specific query (with a better name of course)?

Any other suggestions are surely welcome as well.

EDIT:

The POJO:

@Entity
@Table(name = "web_image")
public class WebImage {
   private long id;

   private Document document;

   private Integer mediaType;

   private Integer width;

   private Integer height;

   private Date creationDate;

   private Date modificationDate;

   private String origUrl;

   private ImageStatus status;

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   @Column(name = "id")
   public Long getId() {
      return id;
   }

   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "document_id")
   public Document getDocument() {
      return document;
   }

   public void setDocument(final OBDocument document) {
     this.document = document;
   }

   @Column(name = "width")
   public Integer getWidth() {
      return width;
   }

   public void setWidth(final Integer width) {
     this.width = width;
   }

   // Other getters and setters for the rest of the private fields
}

The query:

SELECT b.document_id , b.status , b.orig_file_id, a.min_id as id FROM web_image b, ( SELECT x.document_id, MAX(x.id) max_id, MIN(x.id) min_id FROM web_image x WHERE x.document_id in ( :docs ) GROUP BY x.document_id) a WHERE a.max_id = b.id

Avi
  • 21,182
  • 26
  • 82
  • 121
  • Can you show us the hibernate mappings/class of the original pojo, the hibernate query. To see what is possible. – K.C. Nov 07 '13 at 09:53
  • @K.C. - Updated. The POJO is only partial to give the idea of how it's built. – Avi Nov 07 '13 at 10:04
  • What is the problem with fetching all fields in scenarios you don't need them? As I see all your fields are of simple small types and the overhead of fetching them is a little. – Amir Pashazadeh Nov 07 '13 at 10:07
  • @AmirPashazadeh - That's true, but there are more fields that I didn't put here, each image contains a lot of meta data. That's ok when working on few objects. But that's a big-data system that handles a lot of objects on a high rate. I prefer to reduce the data fetched from the DB in each query. – Avi Nov 07 '13 at 10:10
  • 1
    As long as you don't have any relationship to other objects or any `LOB` fields, the overhead of fetching more fields is just a little which can be ignored. – Amir Pashazadeh Nov 07 '13 at 10:16

2 Answers2

1

What about this:

@MappedSuperclass
public class ImageStatusAndOrigFilename {
...
}


@Entity
public class WebImage extends ImageStatusAndOrigFilename {
    ...
}

Now you have both classes, the old one is not an entity, but its clients don't know anything about it, and all fetching and persistings are on WebImage class, but you can query for ImageStatusAndOrigFilename.

Amir Pashazadeh
  • 7,170
  • 3
  • 39
  • 69
1

If it is really necessary to avoid loading unused columns - and you would need to profile to determine if any saving is actually worth it - then a simple solution is simply to write a query using a JPA Contructor Expression.

JPQL:

http://docs.oracle.com/html/E24396_01/ejb3_langref.html#ejb3_langref_constructor

Criteria API:

http://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/querycriteria.html#querycriteria-typedquery-multiselect

Problem with these is you need to add a constructor for each set of properties.

Hibernate specific option - no Constructors required:

See Transformers.aliasToBean:

Java - Hibernate criteria.setResultTransformer() initializes model fields with default values

For all of these options you can use some other DTO or use your existing Entity i.e. return an unmanaged instance.

Lazy Loading

You can also lazy load fields however that required byte code manipulation:

http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/performance.html#performance-fetching-lazyproperties

Also note the comments:

Hibernate3 supports the lazy fetching of individual properties. This optimization technique is also known as fetch groups. Please note that this is mostly a marketing feature; optimizing row reads is much more important than optimization of column reads. However, only loading some properties of a class could be useful in extreme cases.

Community
  • 1
  • 1
Alan Hay
  • 22,665
  • 4
  • 56
  • 110