1

I have a table which contains item descriptions. Items have a price history which can be very extensive. It's that last bit that leads me to avoid using a normal one-to-many Hibernate mapping with lazy loading. Think a price history like ticks on a stock exchange, lots of history.

So I have a cache which works well, it's all wired with Spring, the DAO is injected, the cache manages what needs to be queried vs what it already knows.

So, the "natural" thing is to be able to ask an item about it's price history. Here's some code, which is a slimmed down version of the real thing:

@Entity @Table(name="item")
public class Item {
    @Id
    @Column(name="id")
    private long id;
    @Column(name="name")
    private String name;

    @Autowired
    private PriceCache priceCache;

    /* ...setters, getters for id, name ... */

    public NavigableMap<LocalDateTime,SecurityValue> getPrices(LocalDateTime begTime, LocalDateTime endTime) {
        return priceCache.get(id, begTime, endTime);
    }
}

My original version used all static methods with PriceCache; I want to switch to using an injected bean in part because it will mean I can rewrite the cache as an implementation of an interface which makes it easier to set up unit tests for some bits that aren't in the example; I can create a test cache object that supplies my price history in whatever way I need to the test without ever going to the database.

The problem is that when Spring and Hibernate scan the packages, they seem to conflict over what to do with that @Autowired field; I get the following with some formatting for readability); dbEMF is my EntityManagerFactory:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException:
   Error creating bean with name 'dbEMF' defined in class path resource [applicationContext.xml]:
     Invocation of init method failed;
   nested exception is javax.persistence.PersistenceException:
     [PersistenceUnit: default] Unable to build Hibernate SessionFactory;
   nested exception is org.hibernate.MappingException:
     Could not determine type for: com.example.cache.PriceCache, at table: item, for columns: [org.hibernate.mapping.Column(priceCache)]

Again, the basic code and cache work fine provided I use only static methods with the PriceCache, where I create it as a singleton "manually". Converting it to let Spring handle the creating and injection elsewhere works just fine, too. It's only when I have this mix of Hibernate and Spring that I run into a problem.

I haven't tried going back to using an external XML file for the hibernate config which might solve the issue, or not.

Is there a way to tell Hibernate this is not a column? Or is there a different pattern I should be following to do this sort of thing, maybe some sort of proxy for the Item objects?

  • Based on the comments, I've posted a follow-up question, https://stackoverflow.com/questions/60856510/spring-and-hibernate-mash-up-object-that-is-proxy-of-an-entity-with-an-extra – Roland Roberts Mar 25 '20 at 20:01

2 Answers2

3

you can use @Transient annotation, to indicate it should not be persisted into DB.

Generally speaking, I think if this is an entity, it should not have any autowired cache which is not part of it, but that's a different story

Nir Levy
  • 12,750
  • 3
  • 21
  • 38
  • I think I understand why you think that, and that's kind of the point of my asking about a different pattern. From a business perspective, it makes perfect sense to ask an item about it's price history. That history belongs-to the item. But from a performance and use-case perspective, it's "bad" to let Hibernate handle that directly. – Roland Roberts Mar 25 '20 at 16:47
  • but hibernate will lazy-load for you the price history once asked for – MarcinL Mar 25 '20 at 16:49
  • 1
    @RolandRoberts it may be thought of as "price history belongs to item", but is an item a discrete item or is it a "continuous flow of the item" containing previous prices and other state? Nir Levy is right, an entity should not contain much else besides JPA annotations. That cache should be injected into service classes, not the entity. – Kayaman Mar 25 '20 at 17:05
  • @MarcinL, you didn't read my message :-|. There are hundreds of thousands of rows of price history of the typical request needs mere hundreds. Even with an Oracle Exadata database, it takes minutes per request when I let Hibernate lazy load them. As far as I'm concerned, it's not lazy enough. That's why the PriceCache allows asking by a range of date-times. – Roland Roberts Mar 25 '20 at 17:09
  • @Kayaman, okay, I'll buy that. At some level, the cache _is_ a service container. And I've marked them that way so the DAOs get injected correctly. There is an ItemCache service which returns items. One I get an item, I want to ask about some segment of it's price history. It's not that the _full_ price history doesn't belong, it's just that it's a performance bottleneck for most use cases. – Roland Roberts Mar 25 '20 at 17:18
  • It sounds like I really want to redesign and push my current Item class down a level, call it ItemEntity. Then what I want to think of as an Item is a Service that wraps the ItemEntity and adds the price lookup. – Roland Roberts Mar 25 '20 at 17:24
  • @RolandRoberts I think so too. I believe your current approach is "1. Get item 2. Item, give me prices." whereas it should be "1. Get item 2. Cache, give me prices for this item.", and this happening on the service layer, and the cache and the entity are no longer dependent on each other. – Kayaman Mar 25 '20 at 17:26
  • I think I'm going to have to create a follow-up question. I'm finding myself walking in circles trying to figure out how to do this; if I push down Item -> ItemEntity, then what I want is not really a service, but another entity that proxies Item but adds a service...by using another service. I really want the use model to follow the natural way users think about the problem while hiding the ugliness. – Roland Roberts Mar 25 '20 at 19:09
  • @RolandRoberts, well why not to drop this mapping and leave only the direction from Price to Item? And have a dedicated PriceCache / PriceService that will load the necessary data for you for the given item i.e. PriceCache.findByItem(Item, SomeAdditionalFilters) – MarcinL Mar 26 '20 at 14:59
0

Regarding the discussion in the answer below. Hibernate collections and entities are all proxied objects. Maybe what you can try is to implement a custom HibernateProxy that will manage your collection. As per the docs it seems to be possible (CustomProxies) but I have never done it check here: https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#entity-proxy

MarcinL
  • 130
  • 1
  • 6
  • I ended up going a different route that I _think_ is more in keeping with how Spring wants to be used, see [Spring and Hibernate mash-up, object that is proxy of an @ Entity with an extra @ Service added on](https://stackoverflow.com/questions/60856510/spring-and-hibernate-mash-up-object-that-is-proxy-of-an-entity-with-an-extra) where I ended up answering my own question. – Roland Roberts Mar 27 '20 at 13:50