Spring, Hibernate and multiple references to the same row in database - spring

I'm currently facing a problem here and I thought maybe some of you guys could help me out.
I am developing an application using SPRING and HIBERNATE and my issue is the following:
1 - I first create a new AudioFile object (Domain class) in my main object, and insert it in the database using the hibernateTemplate.save(audioFile) method in the DAO.
2 - I then use hibernateTemplate.get(1) method of the DAO to retrieve the row from the database, and store the instance into an audioFile1 object. (Of type AudioFile)
3 - I then repeat step 2 and store the instance into an audioFile2 object.
In summary: my main class goes like this:
ApplicationContext context = new ClassPathXmlApplicationContext(
"application-context.xml");
AudioFileServices audioFileServices = (AudioFileServices) context.getBean("audioFileServices");
AudioFile audioFile = new AudioFile();
audioFile.setAlbum("test");
audioFile.setArtist("test");
audioFile.setLength(1);
audioFile.setPath("test");
audioFile.setTitle("test");
AudioFile a1 = null;
AudioFile a2 = null;
try {
audioFileServices.saveAudioFile(audioFile);
a1 = audioFileServices.getAudioFile(audioFile.getIdAudioFile());
a2 = audioFileServices.getAudioFile(audioFile.getIdAudioFile());
} catch (Exception e) {
e.printStackTrace();
}
ISSUE: Both audioFile1 and audioFile2 are representing the same object, (The same row in the database) however, they are two different instances and not two objects referencing the same instance. (As I wish it would be) Therefore, any modification made to the first object does not affect the second one. (Even though they are both mapped to the same database row)
I have properly configured the application context to inject the AudioFileDAO bean into the AudioFileService bean and am using the Service layer architecture. Everything works perfectly except for this annoying issue. I have also set up transactions and am using the sessionFactory bean that is injected into the AudioFileDAO bean by SPRING as well.
Do any of you know what I am missing here? Maybe it is a HIBERNATE configuration issue?
Any ideas?
Thank you in advance. (I can provide a copy of my application-context, service, and dao code if needed)

How about set a2 = a1, instead of getting from method getAudioFile.

This is likely because Hibernate is not caching retrieved instances of your objects. I would look into Hibernate Configuration

Related

How to get nested objects inside entity

I'm new with unit tests. I'm trying test Service Layer in a SPRING APP.
Good, i have any relationships in my Service.
VirtualDatacenterModel vdc = vdcRepository.findById(vmDTO.getVdc()).orElseThrow(() -> new ClientException("Invalid VDC id"));
DataCenterModel dc = vdc.getDatacenter();
String vmName = vdc.getTenant().getName() + "_[" + vmDTO.getName() + "]";
In my test i used MOCKITO, dependencies already is mocked, then i cannot see where is wrong
CreateVmDTO vmDTO = Mockito.mock(CreateVmDTO.class);
VmModel vm = Mockito.mock(VmModel.class);
VirtualDatacenterModel vdc = Mockito.mock(VirtualDatacenterModel.class, Mockito.RETURNS_DEEP_STUBS);
TenantModel tenant = Mockito.mock(TenantModel.class);
Mockito.when(vmRepository.save(vm)).thenReturn(new VmModel());
Mockito.when(vdcRepository.findById(vmDTO.getVdc())).thenReturn(Optional.of(new VirtualDatacenterModel()));
Mockito.doReturn(tenant).when(vdc).getTenant();
Mockito.when(vdc.getTenant().getName()).thenReturn("Olivia");
VmModel vmReturn = vmService.createVM(vmDTO);
And i receive NullPointerException, i probably don't know how to use Mockito correctly
You can only mock one action at the time, the following line will certainly be a problem:
Mockito.when(vdcRepository.findById(vmDTO.getVdc())).thenReturn(Optional.of(new VirtualDatacenterModel()));
cause vmDTO.getVdc() will return a null pointer. (vmDTO is a mocked object itself, and has no instruction set for that call). Assuming vmDTO.getVdc() returns the vdc, you can fix as follows:
CreateVmDTO vmDTO = Mockito.mock(CreateVmDTO.class);
VmModel vm = Mockito.mock(VmModel.class);
VirtualDatacenterModel vdc = Mockito.mock(VirtualDatacenterModel.class, Mockito.RETURNS_DEEP_STUBS);
//example fix:
Mockito.when(vmDTO.getVdc()).thenReturn(vdc);
TenantModel tenant = Mockito.mock(TenantModel.class);
Mockito.when(vmRepository.save(vm)).thenReturn(new VmModel());
//also you can do directly:
Mockito.when(vdcRepository.findById(vdc)).thenReturn(Optional.of(new VirtualDatacenterModel()));
Mockito.doReturn(tenant).when(vdc).getTenant();
Mockito.when(vdc.getTenant().getName()).thenReturn("Olivia");
VmModel vmReturn = vmService.createVM(vmDTO);
Also the naming of your methods throws me off:
You have a method vdcRepository.findById, but your input is a vdc object, and not an id? Either the naming is confusing or your input is wrong. If getVdc returns an Id, then you can fix the code by mocking the return of an Id (rename the method to getVdcId or something).
Note: Personally, I seldom mock a DTO. It is just as easy to make the real DTO object, since they often come with a builder or getter/setter.

ReactiveMongoTemplate .save not working on adding #compondindex annotation to the collection class

I have a Java class "Class A" whose object is being saved in a mongoDB using reactiveMongoTemplate.save operation in a reactive way.
public class A {
// field 1
// field 2
..
// field n
}
This had been working perfectly fine and populated a lot of data to the corresponding collection.
Now I decided to introduce indexes to the collection, and hence added this annotation to the class
#CompoundIndex(name ="collection_index_name", def = "{'field1':1, 'field1':-1}", unique=true)
Now after adding this annotation, I see my mongoDB writer( a process which writes data to above mongodb collection ) getting stuck for long duration (15-20mins) or not processing anything further.
On debugging it, I see that the control reaches till the point I have ReaciveMongoTemplate.save() operation. But after the save reactive method is executed, I just get the below warning, and no writes happen to the collection.
Automatic index creation will be disabled by default as of Spring Data MongoDB 3.x.
Please use 'MongoMappingContext#setAutoIndexCreation(boolean)' or override 'MongoConfigurationSupport#autoIndexCreation()' to be explicit.
However, we recommend setting up indices manually in an application ready block. You may use index derivation there as well.
> -----------------------------------------------------------------------------------------
> #EventListener(ApplicationReadyEvent.class)
> public void initIndicesAfterStartup() {
>
> IndexOperations indexOps = mongoTemplate.indexOps(DomainType.class);
>
> IndexResolver resolver = new MongoPersistentEntityIndexResolver(mongoMappingContext);
> resolver.resolveIndexFor(DomainType.class).forEach(indexOps::ensureIndex);
> }
> -----------------------------------------------------------------------------------------
As soon as I remove the above annotation or replace with a fresh new empty collection, and again run the same code, I see entries being saved immediately.
Is there any explanation for this behavior? Has this something to do with adding index to a collection, after a lot of data was already populated inside the collection?

Assigning NamedStoredProcedureQuery at runtime

I have stored procedures that need to be called to perform the operation. However, for each operation, I have saved the stored procedure name into the database. So, at runtime, based on the operation name I will call the assign stored procedure.
Question:
1. How can I set the name of the NamedStoredProcedureQuery at runtime?
I am using Spring JPA with Spring boot.
#NamedStoredProcedureQueries({
#NamedStoredProcedureQuery(
name = "sptest",
procedureName = "usp_helper_test",
resultClasses = {Config.class},
parameters = {
#StoredProcedureParameter(
name = "data",
type = String.class,
mode = ParameterMode.IN)
})
})
In this above example, I want to set procedureName at runtime.
If we ignore stuff like byte code generation: You can't.
Named stored procedures get their name from the annotations you showed in the question.
Of course you can still use either the EntityManager or JDBC (possibly via the JdbcTemplate) to call stored procedures by the name they have in the database.
With the EntityManager you'd invoke EntityManager.createStoredProcedureQuery in one of it's variants.
For the JdbcTemplate approach you can consult this SO answer.
The code you need to write would go in a custom method implementation.

Will Spring JPA repository save() do an update, sometimes?

I've two JPA entities, say A and B. Entity A composes B and has a OneToOne mapping with cascade=MERGE, orphanRemoval=false, etc. The persistence execution is as below
Create B as, B b = new B() and set only pre-persistence data, some date info is automatically set when object is saved.
Create A as, A a = new A() and set all mandatory fields and also set b to a.
respotory.save(a)
Everything is good. Both A, B are saved and good.
Next, fetch A using repository (A oldA = repo.findOne(key)). Transform oldA into another similar application object, say, appA (of Type A2) and do some application logic and then create a NEW object a1 of type entity A and map all the data from oldA.
Here the object a1 has exact similar data as fetched oldA, BUT the only different is, a1 composes a NEW B() object, say b1 and b1 has the same key as 'b' but is missing some mandatory dates (which were supposed to be set while saving, as a result of jpa annotations)
Here I've a couple of queries when saving new entity.
When I save the entity a1, I'm getting an exception as b1 is missing mandatory attribute. I'm not getting how it worked first time and not now?
Also, Does hibernate compare the keys of both a1 and oldA while saving a1 and executes an update() rather than an insert().?
If it is the update operation this time, then only I can get an exception as mandatory data missing on 'b1' as one of the mandatory attribute is supposed to be set during #PrePersist, not while #PreUpdate.
Why would hibernate does an update and NOT an insert ? I created NEW objects for A and B, second time.
Thanks
Hibernate will check, if the id field is null. If so, then it will insert the data, if the id field has a value, Hibernate will do an update to the row with that id. So if you create a new object and copy values from another object, and you want to create a new entry in the database, make sure the id field stays null.

How do I determine programmatically if ehcache is running?

I have a large java application that is configured to use JPA and Hibernate. It is also supposedly configured to use ehcaching for both entity and query caching. However, I have sql logging turned on and no entities are being cached. All of the entity queries are happening on every request.
How can I determine at runtime if it is even running ehcache and whether it thinks an entity should be cacheable?
I didn't write this app so I'm stuck a bit here.
It uses declarations for the caching on the classes.
It is correctly using all the other declarations for Hibernate to perform the read/write operations.
Try something like this:
List<CacheManager> tempManagers = CacheManager.ALL_CACHE_MANAGERS;
System.out.println("# of CMs : " + tempManagers.size());
for (CacheManager tempCM : tempManagers) {
System.out.println("Got: " + tempCM.getName());
String[] cacheNames = tempCM.getCacheNames();
for (int i = 0; i < cacheNames.length; i++) {
String cacheName = cacheNames[i];
System.out.println(cacheName+" - "+ tempCM.getEhcache(cacheName).getStatistics().toString());
}
}
The short answer - a debugger.
Put a breakpoint where you load an entity and follow it down the stack. See if it ever even attempts to get the object from EHCache. Also, check to see if it tries to put it in the cache after it fetches it from the DB.
I implemented this in that way:
public boolean areCachesDefined() {
return this.cacheManagers.stream()
.anyMatch(cacheManager -> cacheManager.getCacheNames().iterator().hasNext());
}
where cacheManagers is a collection with cache handlers per cache type (for example ehcache)
Solution by #mglauche is pretty good. Additionally during startup you can search if your logs are printing following :
o.s.c.ehcache.EhCacheManagerFactoyBean : Initializing EhCache CacheManager

Resources