is it possible to declare an entity property so that it is reset to some default value, but only if the server hosting the ejb.jar is restarted?
the properties are used to to reflect the state of some connected clients that will be disconnected on restart.
Perhaps i should aprouch this problem with a different solution, but think the question is still valid.
How about if you have a hook that is triggered on application start/stop? Like ServletContextListener? On stop save the entries(users) interested in into a intermediar DB table and on start just re-connect.
I went with #Singleton and #Startup annotated EJB, tests seem to work
#Singleton
#Startup
public class ClientResetBean {
// Injected database connection:
#PersistenceContext private EntityManager em;
#PostConstruct
private void resetClientState() {
Query query = em.createQuery("UPDATE Client c SET c.online = false");
query.executeUpdate();
}
}
Related
I am using JPA with Hibernate as the provider on WebSphere.
Is there any way to fire a SQL statement when a new session is opened against the database. Ideally I would want to say when a new Connection is taken out of the pool and given to a thread to use I want to fire a statement.
The basic use case is it's a SET VARIABLE command which should be fired per logged on user (it's passing the userid of the user to the database, rather than the technical user which WebSphere's datasource is authenticating with).
So the SQL statement needs the logged in user id from the application. The statement cannot be configured to fire on the Application Server itself. All of my EntityManager interaction looks the same i.e.
#Stateless
#LocalBean
#Resource(name = "jdbc/ANY", type = javax.sql.DataSource.class,
authenticationType = AuthenticationType.CONTAINER, shareable = true)
public class MyRepository {
#PersistenceContext
private EntityManager entityManager;
I'm using JPA, but I need to unwrap my EntityManagerFactory, so I can add an interceptor to the Session. Afterward I want to wrap the Session back to a EntityManager.
"Why not just use Session instead of EntityManager?" We still want to reduce the impact of a possible technology migration
For what do I want to use Interceptor:
I can resume the problem in the following way: The project works running queries on a alarms database. Each place has one database with a Alarm Table, but the client wants to have a single database, where we will have to create multiple "Alarm Table", one for each place (ex: Table_Alarm-Place1, Table_Alarm-Place2). That means we would have multiple tables for the same entity, the interceptor has the goal of changing the Table name generate by hibernate in the final SQL
How I pretend to use the interceptor:
public class SqlInterceptor extends EmptyInterceptor {
private String tableSufix;
private static final Logger LOGGER = LoggerFactory.getLogger(SqlInterceptor.class);
public SqlInterceptor(String tableSufix) {...}
#Override
public String onPrepareStatement(String sql) {
String finalSql;
//Manipulated SQL (parsed by Hibernate)
return finalSql;
}
}
The project uses JPA 2.1 and Hibernate 4.3.11.Final
A super easy way to override Hibernate's EmptyInterceptor is to just this in properties file
spring.jpa.properties.hibernate.session_factory.interceptor=<fully-qualified-interceptor-class-name>
Cheers :)
You can supply the Interceptor when building the EntityManagerFactory:
String persistenceUnitName = ...;
PersistenceUnitInfo persistenceUnitInfo = persistenceUnitInfo(persistenceUnitName);
Map<String, Object> configuration = new HashMap<>();
configuration.put(AvailableSettings.INTERCEPTOR, new SqlInterceptor());
EntityManagerFactoryBuilderImpl entityManagerFactoryBuilder = new EntityManagerFactoryBuilderImpl(
new PersistenceUnitInfoDescriptor(persistenceUnitInfo), configuration
);
EntityManagerFactory emf = entityManagerFactoryBuilder.build();
It looks like you want to have a multi-tenant database. I had a similar issue before and implemented an interceptor using aspectj to set the filter properly. Even if you do not go for the filter option, you can grab te session everytime it is created using aspectj, as below.
public privileged aspect MultitenantAspect {
after() returning (javax.persistence.EntityManager em): execution (javax.persistence.EntityManager javax.persistence.EntityManagerFactory.createEntityManager(..)) {
Session session = (Session) em.getDelegate();
Filter filter = session.enableFilter("tenantFilter");
filter.setParameter("ownerId", ownerId);
}
}
In the sample below, I just set the filter that needs to be configured on the entity you need to filter:
#Entity
#FilterDef(name = "tenantFilter", parameters = #ParamDef(name = "ownerId", type = "long"))
#Filters({
#Filter(name = "tenantFilter", condition = "(owner=:ownerId or owner is null)")
})
public class Party {
}
Of course, to use the filter instead of the table name, you will have to add a column to differentiate the tables - which I believe to be better than having multiple table names.
You can store all the necessary information in a static ThreadLocal instance and read it afterwards.
This way you avoid the complications with session scoped interceptors and you can use other mechanisms to achieve the same goal (for example with a session factory scoped interceptor which is a bit easier to configure).
Why not simply do the following:
EntityManagerFactory entityManagerFactory = // created from somewhere.
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
// do whatever you need with the session factory here.
// Later in your code, initially use EntityManager and unwrap to Session.
EntityManager entityManager = entityManagerFactory.createEntityManager();
Session session = entityManager.unwrap(Session.class);
Basically, rather than trying to get a Session and then wrapping it back into an EntityManager, simply pass a JPA EntityManager around and unwrap it to Session on an as-needed basis.
I have a JSF managed bean in session scope that contains an entity to trace, for example, the authenticated user:
#ManagedBean
#SessionScoped
public class LoginController implements Serializable {
User user;
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
/* ... */
}
In another bean I have to inject the user to use it to retrieve a list of roles in association with it, like this:
#ManagedBean
#ViewScoped
public class AnotherController implements Serializable {
List<Role> roles;
#ManagedProperty(value="#{loginController.user}")
User user;
public someMethod() {
/* Some stuff that insert other roles into database, referring the user as owner */
roles = user.getRolesList();
}
}
If I update the page with ajax using someMethod, the roles list still not reload.
If I insert em.refresh(user) before user.getRolesList I get this error:
Can not refresh not managed object: model.User[ id=1 ].
Can anyone help me to understand this? Why session scoped entity get not managed if injected into another bean? How can I fix this?
Thank you.
In order for the entitiy to be able to refresh, it needs to be managed, but you know that already.
For it to be mananged, it needs to be either
refetched
merged and then refreshed
remain managed with an extended persistence context
First two options require a transaction.
Since neither #ManagedBean nor #ViewScopedimply any kind of transaction management, the entities in these beans will always be detached, so the behaviour you are experiencing is the expected JPA behaviour.
For the first two options you can pass your request to a transaction-enabled EJB in the backend, which will either merge and update the entity or return a freshly fetched one. If you are not using a Java EE application server, you can use a UserTransaction.
For the third option, you could use an extended persistence context, which does not get closed after each transaction, so the entities remain managed across transaction borders.
EDIT The simplest option for fixing this, using UserTransaction and assuming dependency injection.
#Inject
UserTransaction tx;
//merging and refreshing
tx.begin();
User managedUser = em.merge(user);
em.refresh(managedUser);
tx.commit();
user = managedUser;
//refetching
tx.begin();
user = em.find(User.class, user.getId);
tx.commit();
First of all, you shall separate your concerns, which basically means that your backing bean should not be performing any business logic directly (which is implied by your comment /* Some stuff that insert other roles into database, referring the user as owner */). Instead, you could inject a UserService class in your managed bean with #EJB annotation and call its methods via actions of your commandComponents.
Next, when you get your entity from a database and detach it from persistence context (read: persistence context is closed at the end of the transaction) it means that the entity is not managed by your persistence service anymore and changes to your entity will not be reflected in the database. If you would want to do so, you will need to call EntityManager.merge(), to make the entity persistent. You will need to do so when you want the canges in your LoginController.user object to be persisted to the database.
So, what Kostja says is when you want to get an up-to-date correspondence between your user object and the row in the database you should make the object managed by your EntityManager, by following one of the ways that he proposed.
I am trying to create a bean which manipulates database via JPA. The methods are all correctly annotated with #Transactional(readOnly = false) - until now this was handled by calls from Servlet and everything worked well.
Now I want to do some database manipulation in its init method:
#Component
public class MyBean {
#PostConstruct
#Transactional(readOnly = false)
public void init() {
MyEntity myEntity = ...;
...
em.persist(myEntity);
}
(The case is simplified). Like this I am getting exceptions "No session or session was closed". Obviously the transactions are correctly started only when run by requests in Servlets, but not from the actual bean. How can I achieve this even by running from the bean itself?
Thanks.
AFAIK, Spring doesn't use the transactional proxies around your beans to call the PostConstruct methods (which BTW, are not part of the external interface of the bean most of the time).
Try calling the init() method of MyBean from another bean (where MyBean is injected), or even from a ServletContextListener.
Thanks for reading this.
I have 2 MySQL databases - master for writes, slave for reads. The perfect scenario I imagine is that my app uses connection to master for readOnly=false transactions, slave for readOnly=true transactions.
In order to implement this I need to provide a valid connection depending on the type of current transaction. My data service layer should not know about what type of connection it uses and just use the injected SqlMapClient (I use iBatis) directly. This means that (if I get it right) the injected SqlMapClients should be proxied and the delegate should be chosen at runtime.
public class MyDataService {
private SqlMapClient sqlMap;
#Autowired
public MyDataService (SqlMapClient sqlMap) {
this.sqlMap = sqlMap;
}
#Transactional(readOnly = true)
public MyData getSomeData() {
// an instance of sqlMap connected to slave should be used
}
#Transactional(readOnly = false)
public void saveMyData(MyData myData) {
// an instance of sqlMap connected to master should be used
}
}
So the question is - how can I do this?
Thanks a lot
It's an interesting idea, but you'd have a tough job on your hands. The readOnly attribute is intended as a hint to the transaction manager, and isn't really consulted anywhere meaningful. You'd have to rewrite or extend multiple Spring infrastructure classes.
So unless you're hell-bent on getting this working a you want, your best option is almost certainly to inject two separate SqlMapClient objects into your DAO, and for the methods to pick the appropriate one. The #Transactional annotations would also need to indicate which transaction manager to use (assuming you're using DataSourceTransactionManager rather than JpaTransactionManager), taking care to match the transaction manager to the DataSource used by the SqlMapClient.