TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing - spring

I know this question has been asked numerous times but I couldn't find a suiting answer for me.
I've got two entities in a Spring Roo-application which are in a Many-To-Many-relationship, Release and Component.
First I fetch an instance of an existing Release via
selectedRelease = Release.findReleasesByReleaseNumberEquals(version).getSingleResult();
The method above is a Roo-generated finder which looks like this:
public static TypedQuery<Release> Release.findReleasesByReleaseNummerEquals(String releaseNumber) {
EntityManager em = Release.entityManager();
TypedQuery<Release> q = em.createQuery("SELECT o FROM Release AS o WHERE LOWER(o.releaseNumber) LIKE LOWER(:releaseNummer)", Release.class);
q.setParameter("releaseNumber", releaseNumber);
return q;
}
Then I create a new instance of Component and try to assign it to the selected Release
Component component = new Component();
Set<Release> releases = new HashSet<Release>();
releases.add(selectedRelease);
component.setReleases(releases);
component.persist();
Upon trying to execute persist() I get the exception:
TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.Release;
Does anyone have advice regarding this problem?
The mappings look like this:
Release.java:
#ManyToMany(cascade = CascadeType.PERSIST, mappedBy = "releases")
private Set<Component> components = new HashSet<Component>();
Component.java
#ManyToMany(cascade=CascadeType.PERSIST)
private Set<Release> releases = new HashSet<Release>();

The message is clear: you are trying to save an object, component, that references another object that hasn't been saved yet, selectedRelease.
The solution seems to be easy: just save (and flush whenever you want the changes be comunicated to the database) the Release objects before saving the Component.
However, JPA allows you to avoid all these sentences with TRANSITIVE PERSISTENCE, that is, the cascade options in the #...ToMany annotations.
Just a warning in case you have a bidirectional relationship, when you have a mappedBy attribute in one of the #...ToMany associations. In this case, you'll need a convenient method to manage the association, that is, to set both sides of the association.
For instance, an addRelease(Release r) in Component that both adds the release to the component's set, and sets the component (actually, the this instance) to the release passed as parameter.

Related

How to a raise a domain event for the entity when the entity is created in clean architecture

I have a project created using the clean architecture template.
If I want a domain event be raised when a new project is created, where do I add that?
If I have to raise an event whenever a new item be added to a project, I can accomplish that in the Project entity as shown here.
Similarly for MarkCompletion of a ToDoItem as done here.
But its not clear where do I put the code to raise an event when a new Project is created?
One option is doing something like the following in Create End Point here.
newProject.Events.Add(new ProjectCreatedEvent(newProject));
But this is in UI, away from the domain model, and so does not feel right.
The other option is using ef core interceptors. So when ever save changes is called, just raise event as appropriate something like here.
And if I add event in Project ctor, then this is triggered even in case of an update.
public Project(string name)
{
Name = Guard.Against.NullOrEmpty(name, nameof(name));
var newProjectCreatedEvent = new NewProjectCreatedEvent(this);
Events.Add(newProjectCreatedEvent);
}
Are there any better options/patterns?
Any pointer is deeply appreciated.
When you need to raise a domain event on project creation I would create a factory method that publishes the event.
You can use a static method or implement a factory object.
public class Project : BaseEntity, IAggregateRoot
{
public static Project newProject(string name)
{
var project = new Project(name);
var newProjectCreatedEvent = new NewProjectCreatedEvent(project);
Events.Add(newProjectCreatedEvent);
return project;
}
private Project(string name)
{
Name = Guard.Against.NullOrEmpty(name, nameof(name));
}
}

Hibernate Concurrent ManyToMany relationship

I am having troubles when running last test against a Kotin Spring Boot application. One critical object has a ManyToMany relationship to another one:
#Entity
data class Subscriber(
#ManyToMany
#JoinTable(name = "subscriber_message",
joinColumns = [JoinColumn(name = "subscriber_id")],
inverseJoinColumns = [JoinColumn(name = "message_id")],
indexes =[
Index(name = "idx_subscriber_message_message_id", columnList = "message_id"),
Index(name = "idx_subscriber_message_subscriber_id", columnList = "subscriber_id")
]
)
val messages: MutableList<Message>
}
#Entity
data class Message(
)
Messages are added to a subscriber like this:
subscriber.messages.add(message)
save(subscriber)
When a new message arrives, the controller asks the repository to add the message to all existing subscribers by calling the following method:
#Synchronized
fun SubscriberRepository.addMessage(message: Message) {
findAll().forEach {
it.messages.add(message)
save(it)
}
}
We currently use MutableList for this property since we need to add new elements to the list. This type is not thread safe, so I tried the good old Java concurrent set java.util.concurrent.ConcurrentSkipListSet, to which Spring complained saying that this type is not supported.
Is there a better way to stay thread safe in a web application regarding such a relationship. The list of messages is consumed by a subscriber who then clears it from messages it is done with. But because of concurrency, the cleaning process doesn't work either!
I don't think JPA is going to allow it to be you to initialise it with any concurrent versions because hibernate has own implementations like PersistentSet etc for each collection type which it supports.
When adding a message, you already have synchronised method so that should be fine.
Now I guess many threads trying to consume the messages from the retrieved subscriber. So why don't you do modify the subscriber like this before giving it to the threads consume it.
retrieve subscriber with messages
subscriber.setMessages(Collections.synchronizedList(subscriber.getMessages())) and give it to threads (I don't know what is the equivalent in kotlin)
So now subscriber.messages.removeAll(confirmedMessages) will be thread safe

Dynamically initialize multiple data sources in Spring

In my spring application , I need to dynamically initialize multiple data sources based on some values set up in the application configuration.
I am aware of the AbstractRoutingDataSource class provided by spring jdbc library but it helps only when you need to initialize a single data source based on a single look up key value at a time.
Is it possible to extend the AbstractRoutingDataSource and change its behavior to support multiple key look up and data source resolution? Is there any other alternative approach ? Reference
Basically I am trying to achieve something like this through AbstractDataSourceRouter class:
public class DataSourceRouter extends AbstractRoutingDataSource {
#Value("${com.listdb.datasource.switch}")
private short listDBSwitch;
#Value("${com.scoringdb.datasource.switch}")
private short scoringDbSwitch;
#Value("${com.configmaster.datasource.switch}")
private short configDbSwitch;
private List<String> configuredDataSources;
/**
* Determine the current lookup key. This will typically be
* implemented to check a thread-bound transaction context.
* <p>Allows for arbitrary keys. The returned key needs
* to match the stored lookup key type, as resolved by the
* {#link #resolveSpecifiedLookupKey} method.
*/
#Override
protected Object determineCurrentLookupKey() {
if(ListUtil.isListNotEmpty(configuredDataSources)) {
configuredDataSources =new ArrayList<String>();
String listDBString = (listDBSwitch == 1)?DataSources.LIST.toString() : null;
String configDBString = (configDbSwitch == 1) ? DataSources.CONFIGMASTER.toString() :null;
String scoringDBString = (scoringDbSwitch == 1) ? DataSources.SCORING.toString() : null;
/**
* Add all configured data source keys for look up
*/
configuredDataSources.add(listDBString);
configuredDataSources.add(configDBString);
configuredDataSources.add(scoringDBString);
}
return configuredDataSources;
}
}
Any help/suggestions?
This is not really possible with current spring/hibernate versions even if it would be neat to have it that way. If you need multiple data sources and use AbstractRoutingDataSource to achieve this, then one possible solution is to let spring initialize one DB (the default/configuration DB) and add e.g. init.sql script (or flyway/liquibase such if you are more into that) that initializes all other under the same AbstractRoutingDataSource.
This approach works nicely and gives you more control over your (hopefully test!) environment. Personally I like to have more control over DB schema then any auto-initializers can provide, however that's only a taste/style issue.

NHibernate IPreUpdateEventListener doesn't insert to second table

I'm prototyping some simple audit logging functionality. I have a mid sized entity model (~50 entities) and I'd like to implement audit logging on about 5 or 6. Ultimately I'd like to get this working on Inserts & Deletes as well, but for now I'm just focusing on the updates.
The problem is, when I do session.Save (or SaveOrUpdate) to my auditLog table from within the EventListener, the original object is persisted (updated) correctly, but my AuditLog object never gets inserted.
I think it's a problem with both the Pre and Post event listeners being called to late in the NHibernate save life cycle for the session to still be used.
//in my ISessionFactory Build method
nHibernateConfiguration.EventListeners.PreUpdateEventListeners =
new IPreUpdateEventListener[]{new AuditLogListener()};
//in my AuditLogListener
public class AuditLogListener : IPreUpdateEventListener
{
public bool OnPreUpdate(PreUpdateEvent #event)
{
string message = //code to look at #event.Entity & build message - this works
if (!string.IsNullOrEmpty(message))
AuditLogHelper.Log(message, #event.Session); //Session is an IEventSource
return false; //Don't veto the change
}
}
//In my helper
public static void Log(string message, IEventSource session)
{
var user = session.QueryOver<User>()
.Where(x => x.Name == "John")
.SingleOrDefault();
//have confirmed a valid user is found
var logItem = new AdministrationAuditLog
{
LogDate = DateTime.Now,
Message = message,
User = user
};
(session as ISession).SaveOrUpdate(logItem);
}
When it hits the session.SaveOrUpdate() in the last method, no errors occur. No exceptions are thrown. it seems to succeed and moves on. But nothing happens. The audit log entry never appears in the database.
The only way I've been able to get this to work it to create a completely new Session & Transaction inside this method, but this isn't really ideal, as the code proceeds back out of the listener method, hits the session.Transaction.Commit() in my main app, and if that transaction fails, then I've got an orphaned log message in my audit table for somethign that never happened.
Any pointers where I might be going wrong ?
EDIT
I've also tried to SaveOrUpdate the LogItem using a child session from the events based on some comments in this thread. http://ayende.com/blog/3987/nhibernate-ipreupdateeventlistener-ipreinserteventlistener
var childSession = session.GetSession(EntityMode.Poco);
var logItem = new AdministrationAuditLog
{
LogDate = DateTime.Now,
Message = message,
User = databaseLogin.User
};
childSession.SaveOrUpdate(logItem);
Still nothing appears in my Log table in the db. No errors or exceptions.
You need to create a child session, currentSession.GetSession(EntityMode.Poco), in your OnPreUpdate method and use this in your log method. Depending on your flushmode setting, you might need to flush the child session as well.
Also, any particular reason you want to roll out your own solution? FYI, NHibernate Envers is now a pretty mature library.

JPA, Spring nested transactions

I have to persist an object O that contains a list of OL objects in one transaction:
#Entity
public class O {
#OneToMany
private List<OL> olist;
// getters/setters
}
I'm reading O and OL from a file (xml interface) and have to insert them into the DB.
The condition is the following: if an exception is thrown while persisting an OL object, ignore and continue with other OLs:
#Transactional
private persistO(...) {
O o = new O();
o.set(...);
oDao.persist(o);
for (int i = 0; i < olCount; i++) {
OL ol = new OL();
ol.set(...);
try {
olDao.persist(ol);
em.flush();
}
catch(ConstraintViolationException ex) {
logger.warn()...;
}
}
}`
The problem is that at the first ConstraintViolationException the transaction is set to rollbackOnly and nothing gets persisted.
org.springframework.transaction.TransactionSystemException:
Could not commit JPA transaction;
nested exception is javax.persistence.RollbackException:
Transaction marked as rollbackOnly
Question: How can this be achieved with JPA (Hibernate + Spring)?
Note
I know that it might be possible to first make a query in the DB, make sure that the OL object doesn't exists yet, but let's assume the procedure is very complex and the performance would suffer a lot.
The requirement doesn't allow me to persist the OL objects in new transactions (it
s all or nothing), so #Transactional(propagation = Propagation.PROPAGATION_REQUIRES_NEW) cannot be used.
What you are describing sounds a bit self-contradictory, in terms of transactions. Transactions are meant to be all or nothing. Why would an OL add fail?
Reading between the lines, you are trying to do an add with no overwrite. That sounds a bit suspicious, from a design standpoint. How would there be existing OL without an existing O? You could using SQL to blindly delete any orphan OL, if that's what's going on. This also raises the question whether you. using surrogate keys.
You could add the new O and persist, before adding any OL instances. You could then flush and retrieve the O, which would retrieve any existing OL. You would then add your new OL and persist.

Resources