Can NHibernate load data from 2nd level cache after session is closed? - caching

I am trying stuff I can make with NHibernate for my app. I am using quite a lot of "dictionaries" to store all possible values for certain object properties. I've tried playing with 2nd level cache to store those dictionaries data in there. Now I wonder if there is a way to load needed data from cache after the session is closed. Let's say that is my code:
public class Class1 {
public virtual int Id { get; set; }
public virtual Dic1 Dic { get; set; }
}
public class Dic1 {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
and here are the mappings:
<class name="Class1" table="class1">
<id name="Id" column="id">
<generator class="native" />
</id>
<!-- I want to try not to use fetch="join" here -->
<many-to-one
name="Dic"
class="Dic1"
column="dic1_id"
/>
</class>
<class name="Dic1" table="dic1">
<cache usage="read-write"/>
<id name="Id" column="id">
<generator class="native" />
</id>
<property name="Name" column="name" />
</class>
If I get value of Class1.Dic object before I close the session, NHibernate does not send query to the database, because value was cached by some earlier query.
But let's say, I've closed the session. In debug session, Class1.Dic is an object of Dic1Proxy type and I get an exception when I try to access it/it's properties. Is there a way to load that data after session is closed? 2nd level cache is connected to session factory, so maybe there is a way to actually turn that Proxy into the right object? Or actually force to always load those values without changing fetch method to join.

You can use NHibernateUtil.Initialize(class1.Dic); before closing the session. It will do nothing if the object is not actually a proxy or if it is already loaded, otherwise it will load it (from the second level cache if cached).
You can also force eager fetching while keeping the default select fetch mode: set lazy as false on the many-to-one mapping of the Dic property. Loading a Class1 will then trigger immediately a load of the Dic property. It should fetch it from second level cache if it is in it. Beware that this will cause n+1 loads issues if you query a list of Class1 and if their Dic property is not cached, even if you have enabled batching of lazy loads.
Otherwise if you do not want to do any kind of manipulation on your Dic property before closing the session, you need to change the proxy implementation for it to first check the second level cache before failing if the session is already closed. But this will require too much work to be worth it, in my opinion. (Moreover, what if the entity is missing in the cache anyway? Is it acceptable for your application to fail in such case?)
NHibernate allows you to supply your own factory of proxy factory (IProxyFactoryFactory), by using the proxyfactory.factory_class optional setting, provided you use the default bytecode provider.
Then you would need to implement your own IProxyFactoryFactory, which would likely mostly be a copy of StaticProxyFactoryFactory with BuildProxyFactory yielding a custom proxy factory.
The custom proxy factory would itself likely be mostly a copy of StaticProxyFactory, with GetProxy using a custom ILazyInitializer at this line.
The custom lazy initializer would in turn be likely a copy of LiteLazyInitializer, but with an override of Initialize. Its implementation is here.
That is for the easy part, and till there, that is not as bad as it sounds, that does not involve copying so many lines of code.
Now for the override of Initialize, it would need to check the Session property and acts accordingly, calling its base implementation if the session is usable, or trying to directly load from the second level cache otherwise.
Here you would have more code to duplicate, mainly LoadFromSecondLevelCache and AssembleCacheEntry.
You will also need the persister, which is easy to get if you have the session factory: sessionFactory.GetEntityPersister(EntityName). (ILazyInitializer has an EntityName property.)
As you can see by checking their code, these functions use at many points the session:
CacheMode: checked for ensuring the cache is enabled for the session, you surely should skip that check for your use case.
GenerateCacheKey: easy to inline out of the session, see its code.
Timestamp: using sessionFactory.Settings.CacheProvider.NextTimestamp() instead should do it.
Instantiate: use subclassPersister.Instantiate(id) instead. (Unless you have an interceptor to which it should be delegated.)
The other calls are more troublesome.
You will have to give-up on safe-keeping from circular references as this uses the session persistence context.
Then there is the Assemble and DeepCopy logic which use the session. Many cases just call its Factory property, so depending on the property types of your entities, a dummy session just actually supplying the factory may do.
Skip the readonly stuff if possible, otherwise you will have still more work to do.
Skip most of the persistenceContext stuff: that is the session first level cache. Still there is the InitializeNonLazyCollections call which will be lacking if your entities have some.
About AfterInitialize, this call is currently needed for handling entities having lazy properties (not being an entity or a collection). So you may be able to skip it.
And finally, the PostLoadEvent is there for entities implementing ILifecycle: again, you may be able to skip it, provided you do not use ILifecycle.
If you have also some cached collections to retrieve from the second level cache without a session, you would need to do a similar work with the collection type factory, configurable with the collectiontype.factory_class, providing your own ICollectionTypeFactory yielding collection types overriding Initialize, likewise duplicating the loading from second level cache.
Good luck if you try this.

Related

session is getting reset in IBM Websphere Commerce

I am setting a session in the jsp using scriplet in IBM WCS and setting a value here but when reloading the page the session value is getting lost .
here is how I am setting session attribute
<%
session.setAttribute("testMap", testValue);
%>
However on my local toolkit Its works fine ,but when it is deployed to server having this issue
Please suggest any solution regarding this
Thanks
Ankit
Session state in Websphere Commerce is saved in the Business Context, which is tied to the users ActivityToken.
Session state is serialized to the database, and will be available if the users session goes to another server in the cluster.
You can add your own session state by registering a new context element in BusinessContext.xml in the WC\xml\config\BusinessContext.xml, like so:
<BusinessContext ctxId="MyContext"
factoryClassname="com.ibm.commerce.context.factory.SimpleBusinessContextFactory" >
<parameter name="spiClassname" value="com.myorg.commerce.context.contentimpl.MyContextImpl" />
Then you need to tell which kinds of sessions your Context will be present in
<!-- web site store front configuration -->
<InitialBusinessContextSet ctxSetId="Store" >
...
<InitialBusinessContext ctxId="MyContext" createOrder="0" />
The context will be created along with all other contexts, and will be serialized to either the CTXDATA database table (for known users) and in a browser cookie for anonymous users.
Your context class should look something like this:
An interface class com.myorg.commerce.context.mycontextimpl.MyContext
public abstract interface MyContext extends Context
{
public static final String CONTEXT_NAME = "com.myorg.commerce.context.mycontextimpl.MyContext";
public abstract String getSomeValue();
public abstract void setSomeValue(String v);
}
And an implementation
public class MyContextImpl extends AbstractContextImpl
implements MyContext
{
}
After setting a new value, use "this.setDirty(true)" to flag the changes for persistance.
You must also override getContextAttributes to return the values of your context that needs to be serialized, and the setContextAttributes to re-establish the values.
The point is, that the context does more than simply store values. You put invariants in the context, that should hold true for all aspects of the users interaction with the site. The best example is the EntitlementContext, which holds which contract(s) you are buying under, which can be rather complicated to calculate.
Anyway, to access your context from a command, you'd use
this.getCommandContext().getContext(MyContext.CONTEXT_NAME);
And from a jsp
if (request.getAttribute("myContext") == null) {
request.setAttribute("myContext", ((CommandContext) request.getAttribute("CommandContext")).getContext(MyContext.CONTEXT_NAME));
}
after which you can use it as
${myContext.someValue}
The short answer is don't do this. WebSphere commerce is typically deployed in a distributed environment, and you might be seeing the effect of this when your code gets deployed. It is a lot of work for the application to persist the session across WebSphere nodes. Instead use a cookie, or create a database table. What are you trying to store in that map that has to be in session.

Storing session data in controller

I'm new to Spring. I'm working on a MVC application that would works as follows:
1) user fills the form with data necessary to create the connection to some service
2) controller gets the data from input, create new object serviceManager and save this object e.g in some HashMap with serviceId
3) next time user wants to use this service, controller using serviceId reads data from HashMap.
So I simply need to store this HashMap throughout the whole session in my controller for future use. What would be the best way to accomplish that? Maybe creating serviceManager object each time and reading data from database is the proper solution? In my controller I'm already using #Autowired fields which perfectly serve the purpose, but they're defined in spring xml and I have to store the data dynamically.
Seems your requirement is kind of same with mine which I should keep the main data in the session and every time get the detail data from client and combine 2 kind of data to retrieve something from database. I just put the main part data in the session and then in the whole session that I can get it. I also try to use #SessionAttribute, but after tried dozens of time, I gave it up, it has a lots of problems. So if you can, I just recomment you to store the data in session, that's the samplest way.
I'm newish to spring myself, but as far as putting this in the session:
#Controller
#SessionAttributes({"myObject"})
public class MyController() {
...
#RequestMapping(value="/foo")
// Corrected as per Costi below
// public String someMethod(#PathVariable MyObject myObject) {
public String someMethod(#ModelAttribute MyObject myObject) {
...
}
}
#SessionAttributes will put a MyObject named myObject into the session, if it's not already there, and the #PathVariable pulls it down so you can use it in the method.
The curlys in session attributes aren't necessary for just one attribute, however, you can specify more than one, comma separated, when you use the array notation (which is to say: the curlys)

Hibernate will not persist data after save

Can someone explain why the "lastAccessed" date does not get saved to the database in this example and how I can get it to save to the DB? My understanding is that the do object is an attached object after the save() call and therefore all modifications should be persisted automatically.
Note: "myDate" is persisted correctly, so all other spring configuration seems to be correct.
#Transactional(readOnly = false)
public DateObject getOrCreateDateObject(Date myDate) {
DateObject do = null;
do = getCurrentDateObject(); // For my tests, this has been returning null
if (do == null) {
// create a new object
do = new DateObject();
do.setDate(myDate);
sessionFactory.getCurrentSession().save(do);
}
// This does not persist to the database
do.setLastAccessed(new Date());
return do;
}
I have also tried some of the following combinations (and more) after the save() call. None of these work:
sessionFactory.getCurrentSession().merge(do); // tried before and after do.setDate(d2)
sessionFactory.getCurrentSession().update(do);
sessionFactory.getCurrentSession().saveOrUpdate(do);
sessionFactory.getCurrentSession().flush();
DateObject doCopy = (DateObject)sessionFactory.getCurrentSession().load(DateObject.class, do.getId());
sessionFactory.getCurrentSession().merge(doCopy);
doCopy.setLastAccessed(new Date());
I'm hoping this is an easy answer that I'm just not seeing. Thank you for your help!
Edit #1 05/22/2012
As requested, here is the mapping for this entity, specified in src/main/resources/META-INF/dateobject.hbm.xml. I can see that the columns are created in the database using "SELECT * FROM dateObjects" in the mysql client. MY_DATE is populated correctly, but LAST_ACCESSED is set to NULL.
<class name="com.example.entity.DateObject" table="dateObjects">
<id name="id" column="DATE_OBJECT_ID">
<generator class="identity" />
</id>
<property name="date" type="date" column="MY_DATE" />
<property name="lastAccessed" type="date" column="LAST_ACCESSED" />
</class>
Edit #2 05/24/2012
I have a working SSCCE at https://github.com/eschmidt/dateobject. The interesting thing is that the web client (calling localhost:8080/view/test) shows that lastAccessed is set correctly, but when I check the database with the MySQL client, it shows that lastAccessed is NULL. With this complete set of code, can anybody see why the database wouldn't update even though the method is marked #Transactional?
If you're absolutely certain that after running that code, do.date is stored in the db and do.lastAccessed isn't, then your connection and transaction are obviously set up correctly. My first guess would be incorrect mappings, since that's the simplest solution. You don't happen to have an #Transient on the field, the getter, or the setter for lastAccessed, do you? (Assuming, of course, that you're using annotations to map your domain objects.)
If you could provide an SSCCE, I'll bet I or someone else can give you a definitive answer.
Update: It's hard trimming a full application down to the smallest possible code that demonstrates a problem. The upshot is that you'll likely find the answer while you're at it. I have lots of sample projects in github that might help guide you if you just need a few nudges in the right direction. basic-springmvc might be closest to what you're doing, but it uses annotations instead of xml for mappings. It's also a Spring MVC project. It's a lot simpler to start a Spring context manually in a main class than to worry about a whole servlet container and the multiple contexts that Spring MVC wants you to have. spring-method-caching, for one, has an example of doing that.
As for the mapping you posted, it looks fine, though it's been a long while since I touched an XML mapping. Are you using field or property access? That could possibly have a bearing on things. Also, are there any custom listeners or interceptors in the SessionFactory that might be twiddling with your objects?
You are using IDENTITY generation for your identifier generation strategy, so the save() call here immediately translates to the insert. Do you see any INSERT/UPDATE/DELETE SQL executed after that? If not, it is most likely that the session is just not being flushed. flushing might happen at a number of points, read the docs on flushing if you are unfamiliar.

lazy loading in granite ds

how will i load my entities in my flex application using lazy loading .I have a deep object graph
GraniteDS, together with its data management framework, lets you transparently load your uninitiated associations: see documentation here. So, basically, you don't have to do anything special in order to initialize your lazy collections/proxies, you only need to access one of them on the client side (asking for the size of a collection for example) and it will trigger a call to the server and fetch the uninitialized data.
If you don't want or can't use transparent lazy-loading, you need to write a specific initialization method which must have access to an EntityManager, receive your entity as a parameter, initialize the required association and send the entity back to the client.
AFAIK it's impossible. You should have opened hibernate session to do this.
In my project I'm doing what you need this way:
I have spring service like this:
public interface SomeObjectManager {
List<SomeObject> getObjects(); // here we have lazy loading for SomeObject's properties
SomeObject getFullObject(long objectId); // here we're loading full object
}
Some properties of SomeObject use lazy loading. To load them I use HQL query like this:
SELECT s FROM SomeObject s
LEFT JOIN FETCH s.child children
LEFT JOIN FETCH children.items items
LEFT JOIN FETCH items.property
WHERE s.id=:id
This query forces hibernate to load properties defined lazy.
So if you don't need fully loaded SomeObject instances you use getObjects() method. If then you need details on concrete SomeObject instance you use getFullObject() method.
Hope this helps.

Extend linq-to-sql partial class to avoid writing a property?

I have a linq-to-sql class. I have a property "Password" for which I want to call the underlying ASP.NET Membership provider. Thus, I do not want this property written out directly but via my own code. I basically want to create a facade/proxy for this property such that I may use the underlying membership provider or a custom stored procedure.
I want to accomplish without modifying the LINQ-TO-SQL designer generated code, if at all possible.
It is possible. You can add your properties and methods to linq generated class using partial class mechanism. Linq generated classes are marked partial so you can add class members with:
public partial class YourLinqClass
{
// your methods and properties. refer linq properites and methods with "this."
// example:
public string Password
{
get
{
int id = this.UserId;
string password = // ... get password
return password;
}
set
{
// ...
}
}
}
You have to place the partial class in the same namespace as the rest of dbml.
The best option is to remove the property from the designer and write it in code, in the partial class, as described by PanJanek.
However, if you do it this way, you are pursuing a bad design. You're introducing a dependency into your entity class that breaks layer encapsulation. Entity classes shouldn't know about providers any more than they know about the DataContext that loads them. They aren't really meant to be anything more than containers for data going in and out of the database.
You should consider making a separate class that wraps the entity, the context, the username provider, and whatever other services you require, and in that class retrieve the username and do the required operations to your entity.
It looks like it might be possible to create a custom DataContext to handle this situation.
http://weblogs.asp.net/scottgu/archive/2007/07/11/linq-to-sql-part-4-updating-our-database.aspx
There are partial methods for the individual properties as, well and an OnValidate method.
In my case, I think the best solution is to throw an exception in the property changing method and add a public method for setting this individual property. This solution while not perfect will avoid touching the SQL generated code, where the property could be set to readonly or the setter removed.
Other suggestions welcome.

Resources