In my web application, when a new user sign-up i need to create a new database in mysql for him. Example: root => db_root, admin => db_admin, user1 => db_user1 and so on. When the user logs into my app, I need to instance a new entityManager based on his new created database and manipulate data only on that db.
One problem is: how am i supposed to know which entityManager instance i should use for each user request. I thought about creating a
Hashmap<String, EntityManager>
save an attribute entityManagerKey on user's session and retrieve the right entityManager by this key. This approach brings along some troubles such as the right time to destroy a non-used entityManager. Moreover, i think it will take all server memory to work.
Though it's seems to be not a great idea, i did't realize another solution. I'm using JPA/Hibernate and Jersey. Any ideas will be appreciated, except creating only one database.
Thanks!
You can use the EntityManagerFactory and pass it the user and password
Map<String, String> properties = new HashMap<String, String>();
properties.put("javax.persistence.jdbc.user", "admin");
properties.put("javax.persistence.jdbc.password", "admin");
EntityManagerFactory emf = Persistence.createEntityManagerFactory(
"some-jdbc-url", properties);
EntityManager entityManager = emf.createEntityManager();
If you think performance is an issue, you can cache the entityManager. Depending on how much traffic you have on your application and the available resources, you can determine the number of cached connections. I wouldn't bother with caching in the beginning, because creating a DB connection is usually not that time consuming, compared to a web request.
Here is the sample code,
private static final Map<String, EntityManagerFactory> ENTITY_FACTORIES = new HashMap<String, EntityManagerFactory>();
public void onStart() {
this.buildEntityManagerFactories();
}
#Override
public void onStop() {
closeEMFactories();
}
private void closeEMFactories() {
try {
if(ENTITY_FACTORIES.size() > 0) {
for(Entry<String, EntityManagerFactory> entityFactoryMgr : ENTITY_FACTORIES.entrySet()) {
EntityManagerFactory entityManagerFactory = entityFactoryMgr.getValue();
if(entityManagerFactory.isOpen()) {
entityManagerFactory.close();
}
}
isLoaded = false;
}
} catch (Exception e) {
Logger.error("Error while shutting down the datasource plugin", e);
}
}
/**
* Create the entityManagerFactory Bean.
* #return entityManagerFactory Bean
*/
public void buildEntityManagerFactories(String userName) {
buildEMFactory(userName, DEFAULT_PERSISTENCE_UNIT);
}
private void buildEMFactory(String userName, String persistenceUnitName) {
if(!isLoaded) {
try{
ENTITY_FACTORIES.put(userName, Persistence.createEntityManagerFactory(persistenceUnitName, XOAPP_DB_PROPERTIES));
}catch(Exception e) {
Logger.error("Error while building the entity manager factory for the persistence unit :" + persistenceUnitName, e);
}
}
}
public EntityManager em(String persistenceName) {
EntityManagerFactory entityManagerFactory = ENTITY_FACTORIES.get(persistenceName);
if(entityManagerFactory != null) {
return entityManagerFactory.createEntityManager();
}
return null;
}
/**
* Get the entityManagerFactory Bean.
* #return entityManagerFactory Bean
*/
public EntityManagerFactory getEntityManagerFactory(String userName) {
EntityManagerFactory entityManagerFactory = ENTITY_FACTORIES.get(userName);
if(entityManagerFactory == null) {
buildEMFactory(userName);
entityManagerFactory = ENTITY_FACTORIES.get(userName);
}
return entityManagerFactory;
}
Related
I need to create an Entity Manager manually to support having multiple datasources. I've realised that by creating it manually (instead of autowiring it as when you have only one single datasource), I actually do need to set all the JPA properties manually too, including what are usually set with default values by Spring. This means all the JPA parameters I specified in application.yaml and what Spring normally set default values for have now to be loaded and set manually.
Is there anyway to create an Entity Manager manually but have it automatically use all the JPA properties in application.yaml?
Here's my code
public LocalContainerEntityManagerFactoryBean entityManager(EntityManagerFactoryBuilder builder, DataSource secondDataSource) {
Map<String, Object> props = new HashMap<>();
// Is there a way to not do this one by one for all of the default JPA properties and what I specified in application.yaml?
props.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
// props.put("prop1", "propvalue1");
// ...
return builder.dataSource(secondDataSource).packages(MyEntity.class).properties(props).build();
}
Let's say, you have a persistence-unit with the name myData
private EntityManager getEntityManager() {
EntityManagerFactory emf = null;
try {
emf = Persistence.createEntityManagerFactory("myData");
} catch (Exception e) {
log.warn("An Error has occurred while creating persistence layer", e);
}
if (emf != null) {
return emf.createEntityManager();
} else {
log.warn("An error has occurred while retrieving Entity Manager from Persistence factory");
return null;
}
}
And use like below
final EntityManager em = getEntityManager();
assert em != null;
EntityTransaction entityTransaction = em.getTransaction();
try {
entityTransaction.begin();
userList.stream().forEach(user -> {
em.merge(RestUtil.convertToDBUser(user));
});
entityTransaction.commit();
log.info("Completed data persistence ");
} catch (RuntimeException e) {
if (entityTransaction.isActive()) {
entityTransaction.rollback();
}
log.warn("An error has occurred while committing JDBC transaction. ", e);
throw e;
} finally {
em.close();
}
I'm new to Hibernate. I've created the SessionFactory and obtaining the Session like below. And I'm using c3p0 connection provider
public static SessionFactory getSessionFactory() {
try {
if (sessionFactory == null) {
Configuration configuration = loadDBConfiguration();
if (configuration != null) {
sessionFactory = configuration.buildSessionFactory();
} else {
log.info("---- Configuration failed ----");
}
}
} catch (Exception ex) {
log.info("---- Initial SessionFactory creation failed ----");
}
return sessionFactory;
}
public static EntityManagerFactory getEntityManagerFactory() {
try {
Session session = getSessionFactory().openSession();
entityManagerFactory = session.getEntityManagerFactory();
} catch (Exception e) {
log.error(e);
}
return entityManagerFactory;
}
public static EntityManager getEntityManager() {
try {
EntityManagerFactory entityManagerFactory = getEntityManagerFactory();
return entityManagerFactory.createEntityManager();
} catch (Exception e) {
log.error(e);
}
return null;
}
And i'm confused with below things.
Should i close the connection after commit(), or c3p0 connection provider will close after commit().
Shall i call getEntityManager() on each request or should i use like singleton
If i'm used as singleton, then does it affect any parallel transaction.begin() or transaction.commit() if server receives multiple request at same time.
Also in singleton the entity still persist in session until it gets closed or clear manually. So what i need to do in this case.
Currently the getEntityManager() gets called in all my EntityManager Classes like UserManager, AccountsManager. After some period of time in RDS it shows like 20connections, even my application not handling any user requests.
You should close your connections. Since your connections are wrapped by the database connection pool, they will not get physically closed, but they will return to the pool. This needs to be done otherwise the connection pool will think the connections are being used and when you hit the pool limit, no new connections will be open.
Keep the session factory as a singleton. You should open a new session for each thread.
Setup: Spring application deployed on Weblogic 12c, using JNDI lookup to get a datasource to the Oracle Database.
We have multiple services which will be polling the database regularly for new jobs. In order to prevent two services picking the same job we are using a native SELECT FOR UPDATE query in a CrudRepository. The application then takes the resulting job and updates it to PROCESSING instead of WAITING using the CrusRepository.save() method.
The problem is that I can't seem to get the save() to work within the FOR UPDATE transaction (at least this is my current working theory of what goes wrong), and as a result the entire polling freezes until the default 10 minute timeout occurs. I have tried putting #Transactional (with various propagation flags) basically everywhere, but I'm not able to get it to work (#EnableTransactionManagement is activated and working).
Obviously there must be some basic knowledge I'm missing. Is this even a possible setup? Unfortunately, just using #Transactional with a non-native CrudRepository SELECT query is not possible, as it apparently first makes a SELECT to see if the row is locked or not, and only then makes a new SELECT that locks it. Another service could very well pick up the same job in the meanwhile, which is why we need it to lock immediately.
Update in relation to #M. Deinum's comment.: I should perhaps also mention that it's a setup wherein the central component that's doing the polling is a library used by all the other services (therefore the library has #SpringBootApplication, as does each service using it, so double component scanning is certainly present). Furthermore, the service has two separate classes for polling depending on the type of service, with a lot of common code, shared in an AbstractTransactionHelper class. Below I've aggregated some code for the sake of brevity.
The library's main class:
#SpringBootApplication
#EnableTransactionManagement
#EnableJpaRepositories
public class JobsMain {
public static void initializeJobsMain(){
PersistenceProviderResolverHolder.setPersistenceProviderResolver(new PersistenceProviderResolver() {
#Override
public List<PersistenceProvider> getPersistenceProviders() {
return Collections.singletonList(new HibernatePersistenceProvider());
}
#Override
public void clearCachedProviders() {
//Not quite sure what this should do...
}
});
}
#Bean
public JtaTransactionManager transactionManager(){
return new WebLogicJtaTransactionManager();
}
public DataSource dataSource(){
final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
dsLookup.setResourceRef(true);
DataSource dataSource = dsLookup.getDataSource("Jobs");
return dataSource;
}
}
The repository (we're returning a set with only one job as we had some other issues when returning a single object):
public interface JobRepository extends CrudRepository<Job, Integer> {
#Query(value = "SELECT * FROM JOB WHERE JOB.ID IN "
+ "(SELECT ID FROM "
+ "(SELECT * FROM JOB WHERE "
+ "JOB.STATUS = :status1 OR "
+ "JOB.STATUS = :status2 "
+ "ORDER BY JOB.PRIORITY ASC, JOB.CREATED ASC) "
+ "WHERE ROWNUM <= 1) "
+ "FOR UPDATE", nativeQuery = true)
public Set<Job> getNextJob(#Param("status1") String status1, #Param("status2") String status2);
The transaction handling class:
#Service
public class JobManagerTransactionHelper extends AbstractTransactionHelper{
#Transactional
#Override
public QdbJob getNextJobToProcess(){
Set<Job> jobs = null;
try {
jobs = jobRepo.getNextJob(Status.DONE.name(), Status.FAILED.name());
} catch (Exception ex) {
logger.error(ex);
}
return extractSingleJobFromSet(jobs);
}
Update 2: Some more code.
AbstractTransactionHelper:
#Service
public abstract class AbstractTransactionHelper {
#Autowired
QdbJobRepository jobRepo;
#Autowired
ArchivedJobRepository archive;
protected Job extractSingleJobFromSet(Set<Job> jobs){
Job job = null;
if(jobs != null && !jobs.isEmpty()){
for(job job : jobs){
if(this instanceof JobManagerTransactionHelper){
updateJob(job);
}
job = job;
}
}
return job;
}
protected void updateJob(Job job){
updateJob(job, Status.PROCESSING, null);
}
protected void updateJob(Job job, Status status, String serviceMessage){
if(job != null){
if(status != null){
job.setStatus(status);
}
if(serviceMessage != null){
job.setServiceMessage(serviceMessage);
}
saveJob(job);
}
}
protected void saveJob(Job job){
jobRepo.save(job);
archive.save(Job.convertJobToArchivedJob(job));
}
Update 4: Threading. newJob() is implemented by each service that uses the library.
#Service
public class JobManager{
#Autowired
private JobManagerTransactionHelper transactionHelper;
#Autowired
JobListener jobListener;
#Autowired
Config config;
protected final AtomicInteger atomicThreadCounter = new AtomicInteger(0);
protected boolean keepPolling;
protected Future<?> futurePoller;
protected ScheduledExecutorService pollService;
protected ThreadPoolExecutor threadPool;
public boolean start(){
if(!keepPolling){
ThreadFactory pollServiceThreadFactory = new ThreadFactoryBuilder()
.setNamePrefix(config.getService() + "ScheduledPollingPool-Thread").build();
ThreadFactory threadPoolThreadFactory = new ThreadFactoryBuilder()
.setNamePrefix(config.getService() + "ThreadPool-Thread").build();
keepPolling = true;
pollService = Executors.newSingleThreadScheduledExecutor(pollServiceThreadFactory);
threadPool = (ThreadPoolExecutor)Executors.newFixedThreadPool(getConfig().getThreadPoolSize(), threadPoolThreadFactory);
futurePoller = pollService.scheduleWithFixedDelay(getPollTask(), 0, getConfig().getPollingFrequency(), TimeUnit.MILLISECONDS);
return true;
}else{
return false;
}
}
protected Runnable getPollTask() {
return new Runnable(){
public void run(){
try{
while(atomicThreadCounter.get() < threadPool.getMaximumPoolSize() &&
threadPool.getActiveCount() < threadPool.getMaximumPoolSize() &&
keepPolling == true){
Job job = transactionHelper.getNextJobToProcess();
if(job != null){
threadPool.submit(getJobHandler(job));
atomicThreadCounter.incrementAndGet();//threadPool.getActiveCount() isn't updated fast enough the first loop
}else{
break;
}
}
}catch(Exception e){
logger.error(e);
}
}
};
}
protected Runnable getJobHandler(final Job job){
return new Runnable(){
public void run(){
try{
atomicThreadCounter.decrementAndGet();
jobListener.newJob(job);
}catch(Exception e){
logger.error(e);
}
}
};
}
As it turns out, the problem was the WeblogicJtaTransactionManager. My guess is that the FOR UPDATE resulted in a JPA transaction, but upon updating the object in the database, the WeblogicJtaTransactionManager was used, which failed to find an ongoing JTA transaction. Since we're deploying on Weblogic we wrongly assumed we had to use the WeblogicJtaTransactionManager.
Either way, exchanging the TransactionManager to a JpaTransactionManager (and explicitly setting the EntityManagerFactory and DataSource on it) basically solved all problems.
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(entityManagerFactory().getObject());
jpaTransactionManager.setDataSource(dataSource());
jpaTransactionManager.setJpaDialect(new HibernateJpaDialect());
return jpaTransactionManager;
}
Assuming you also have added an EntityManagerFactoryBean which is needed if you want to use multiple datasources in the same project (which we're doing, but not within single transactions, so no need for JTA).
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setJpaVendorAdapter(vendorAdapter);
factoryBean.setPackagesToScan("my.model");
return factoryBean;
}
Im working on a java standAlone project. I need to use hibernate in a MultiThread application but i just cant figure it out how to set up this correctly.
Each Thread deals with the same process of the others.
Everything goes Ok when i run it in a Non-Async way, but when i call the same thing using threads, hibernate just don't work fine.
Can anyone please explain me what's the correct way to use Hibernate in a multiThread Java Stand-Alone App?
Hibernate Util
public class HibernateUtil {
private static final Session session;
static {
try {
SessionFactory sessionFactory;
Properties properties = new Properties();
properties.load(new FileInputStream("middleware.properties"));
Configuration cfg = new Configuration().configure();
cfg.addProperties(properties);
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(cfg.getProperties()).build();
sessionFactory = cfg.buildSessionFactory(serviceRegistry);
session = sessionFactory.openSession();
} catch (IOException | HibernateException he) {
JOptionPane.showMessageDialog(null, DataBaseMessage.CONNECTION_ERROR.getMessage(), DataBaseMessage.CONNECTION_ERROR.getTitle(),JOptionPane.ERROR_MESSAGE);
throw new ExceptionInInitializerError(he);
}
}
public static Session getSession() {
return session;
}
The Error comes here
TbHistoDespachos despacho = Dao.findDespachoByTagId(element.getChild("tagID").getText());
public synchronized List<TbHistoDespachos> ExractDespachoAndNotify(String data, String nombreConexion) {
List<TbHistoDespachos> despachos = new ArrayList<>();
String nombreConexionUpp = nombreConexion.toUpperCase();
try {
Document doc = convertStringToDocument(data);
if (!doc.getRootElement().getChild("reply").getChild("readTagIDs")
.getChildren().isEmpty()) {
for (Element element : doc.getRootElement().getChild("reply").
getChild("readTagIDs").getChild("returnValue")
.getChildren()) {
TbHistoDespachos despacho = Dao.findDespachoByTagId(element.getChild("tagID").getText());
if (despacho != null) {
if(evaluateDespacho(nombreConexionUpp, despacho)){
despachos.add(despacho);
}
}
}
}
} catch (JDOMException | IOException ex) {
JOptionPane.showMessageDialog(null, FilesMessageWarnings.NOTIFICATION_SAP_WARNING.
getMessage().replace("&nombreConexion", nombreConexion).replace("&tagID", ""),
FilesMessageWarnings.NOTIFICATION_SAP_WARNING.getTitle(), JOptionPane.WARNING_MESSAGE);
}
return despachos;
}
Here is the DAO
public class Dao {
private static Session sesion;
public static TbHistoDespachos findDespachoByTagId(String tagId) {
TbHistoDespachos despacho = null;
try {
startTransmission();
despacho = (TbHistoDespachos)sesion.createQuery("FROM TbHistoDespachos WHERE TAG_ID =:tagId")
.setParameter("tagId", tagId)
.uniqueResult();
stopTransmission();
} catch (HibernateException he) {
System.out.println("error: " + he.getMessage());
JOptionPane.showMessageDialog(null, DataBaseMessage.QUERY_ERROR.getMessage(),
DataBaseMessage.QUERY_ERROR.getTitle(), JOptionPane.ERROR_MESSAGE);
}
return despacho;
}
private static void startTransmission() {
sesion = HibernateUtil.getSession();
sesion.getTransaction().begin();
}
private static void stopTransmission() {
sesion.getTransaction().commit();
sesion.getSessionFactory().getCurrentSession().close();
sesion.clear();
}
ANY IDEAS?
The problem stems from static Session variables. A SessionFactory is thread-safe and, generally speaking, you only need one (static) instance per database. A Session, on the other hand, is not thread-safe and is usually created (using a SessionFactory) and discarted/closed on the fly.
To solve your immediate problem, remove the static Session sesion variable from your Dao and also 'inline' the startTransmission and stopTransmission methods in the findDespachoByTagId method. This will ensure that each thread calling findDespachoByTagId creates and uses its own session instance. To analyze the current problem, imagine two threads calling findDespachoByTagId at the same time. Now the static session variable will be assigned a value twice by the startTransmission method. This means one session instance is lost almost immediatly after it was created while the other one is used by two threads at the same time. Not a good thing.
But there are other problems too: there are no finally blocks that guarantee transactions are closed and database connections are released (via the closing of sessions). Also, you will probably want to use a database pool as the one provided by Hibernate is not suitable for production. I recommend you have a look at HibHik: I created this project to show a minimal stand-alone Java application using Hibernate with a database pool (HikariCP) that uses the recommended patterns and practices (mostly shown in TestDbCrud.java). Use the relevant parts in your application, than write multi-threaded unit-tests to verify your database layer (DAO) is working properly, even in the case of failure (e.g. when the database is suddenly no longer available because the network-cable was unplugged).
I m using glassfish v2 and persistence in a web application.
I m calling persistence codes using a normal java class file inside a web Application
I can select easily using this code: -
#PersistenceUnit
public EntityManagerFactory emf;
EntityManager em;
public List fname (String id) {
String fname = null;
List persons = null;
//private PersistenceManagerFactory persistenceManagerFactory;
try {
emf = Persistence.createEntityManagerFactory("WebApplicationSecurityPU");
em = emf.createEntityManager();
persons = em.createQuery("select r from Roleuser r").getResultList();
int i=0;
for (i=0;i<persons.size(); i++)
System.out.println("Testing n "+ i +" " + persons.get(i));
} catch(Exception e) {
System.out.println("" + e);
}
finally {
if(em != null) {
em.close();
}
}
return persons;
}
I want to update using JTA as the persistence.xml file has
transaction-type="JTA"
When i try using update using this code i get a nullPointerException without any traces in the log
#PersistenceUnit
public EntityManagerFactory emf;
EntityManager em;
Context context;
#Resource
private UserTransaction utx;
public List fname (String id) {
String fname = null;
List persons = null;
try {
emf = Persistence.createEntityManagerFactory("WebApplicationSecurityPU");
utx.begin();
em = emf.createEntityManager();
int m = em.createQuery("update Roleuser r set r.firstName = 'Jignesh I' where r.userID=9").executeUpdate();
utx.commit();
} catch(Exception e) {
System.out.println("" + e);
}
finally {
if(em != null) {
em.close();
}
}
return persons;
}
Any help
Thanks
Pradyut
Perhaps your bean isn't managed - i.e. it's not part of any context (spring, EJB). How are you creating your object?
You really should not call createEntityManager() - inject one using #PersistenceContext
You must be absolutely sure you need JTA before using it.
You seem to be using PersistenceUnit, but then re-assign the etm - I'd suggest drop both and see p2 above.
If you are not using any dependecy injection at all, then drop all the annotations, retain the current code, and type:
em.getTransaction().begin();
...
em.getTransaction().commit();
(and define RESOURCE_LOCAL in your persistence.xml. You really don't need JTA)
well the code should be without any nightmares...(atleast for me in glassfish)
with the persistence.xml having
<persistence-unit name="WebApplicationSecurityPU" transaction-type="RESOURCE_LOCAL">
the code
#PersistenceUnit
public EntityManagerFactory emf;
public EntityManager em;
public EntityManager getEm() {
emf = Persistence.createEntityManagerFactory("WebApplicationSecurityPU");
em = emf.createEntityManager();
return em;
}
public List fname (String id) {
String fname = null;
List persons = null;
try {
System.out.println("test");
em = this.getEm();
em.getTransaction().begin();
int m = em.createQuery("update Roleuser r set r.firstName = 'Jignesh H' where r.userID=9").executeUpdate();
em.getTransaction().commit();
} catch(Exception e) {
System.out.println("" + e);
}
finally {
if(em != null) {
em.close();
}
}
return persons;
}
Any improvements are welcome...(actually needed...)
(How to go about using #PersistenceContext)
Thanks
Pradyut
Your "normal" class is very likely not a managed component i.e. a class whose life cycle is managed by the container (like Servlets, Servlet Filters, JSP tag handlers, JSF Managed Beans, ...) and can't benefit from resource injection1.
So neither the UserTransaction nor the EntityManagerFactory are injected here, hence the NullPointerException.
Honestly, you should try to use a container managed EntityManager, this would make your code less messy. If you cannot get it injected, get it via a JNDI lookup. See the resource below.
1 Have a look at Web Tier to Go With Java EE 5: A Look at Resource Injection for a nice overview of what can be injected, and where.
Resources
How to use EntityManager API in web module
References
JPA 1.0 specification
Section 5.2 "Obtaining an Entity Manager"
Section 5.6 "Container-managed Persistence Contexts"