I try to configure my EntityManager programmatically. My vendor is OpenJPA and I write a simple console application. Here is my code
public static void main(String[] args) {
Map<String, String> properties = new HashMap<>();
properties.put("openjpa.ConnectionDriverName", "org.postgresql.Driver");
properties.put("openjpa.ConnectionURL", "jdbc:postgresql://localhost:5432/shop");
properties.put("openjpa.ConnectionUserName", "bob");
properties.put("openjpa.ConnectionPassword", "secret");
properties.put("openjpa.RuntimeUnenhancedClasses", "supported");
properties.put("openjpa.MetaDataFactory", "jpa(Types=de.jpa.demo.dto.Category;de.jpa.demo.dto.User;de.jpa.demo.dto.Order;de.jpa.demo.dto.Product)");
properties.put("openjpa.jdbc.SynchronizeMappings", "buildSchema(foreignKeys=true)");
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test", properties);
OpenJPAConfiguration configuration = ((EntityManagerFactoryImpl) factory).getConfiguration();
MetaDataRepository repositoryInstance = configuration.getMetaDataRepositoryInstance();
repositoryInstance.addPersistenceAware(Category.class);
repositoryInstance.addPersistenceAware(Order.class);
repositoryInstance.addPersistenceAware(Product.class);
repositoryInstance.addPersistenceAware(User.class);
manager = factory.createEntityManager();
}
But I get an error
javax.persistence.PersistenceException: No persistence providers available for "test" after trying the following discovered implementations: org.apache.openjpa.persistence.PersistenceProviderImpl
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:182)
at de.jpa.demo.dto.OpenJpaUserRepositoryIT.init(OpenJpaUserRepositoryIT.java:56)
at de.jpa.demo.dto.OpenJpaUserRepositoryIT.before(OpenJpaUserRepositoryIT.java:22)
What I'm doing wrong?
You must have a META-INF/persistence.xml file with a persistence unit named 'test' in it.
Also, please please please remove openjpa.RuntimeUnenhancedClasses property. It is a buggy feature and you'll be much happier if you take the time to figure out another enhancement strategy.
Related
Most amount of information found was in the official docs.
I want to replace the JPA provider completely (use Hibernate OGM)
One solution would be to create a bean for transactions and a bean with emf.
EntityManagerFactory emf = Persistence.createEntityManagerFactory( "id_from_persistence.xml");
But what I am really looking for is to completely integrate another JPA provider into SpringBoot, how possible is that?
#Bean
public LocalEntityManagerFactoryBean entityManagerFactory(){
LocalEntityManagerFactoryBean factoryBean = new LocalEntityManagerFactoryBean();
factoryBean.setPersistenceUnitName("id_from_persistence.xml");
return factoryBean;
}
If it is - i am definitely missing something out, first exception is Entity Manager is not initialized.
As far as I have understood Id need to provide my own implementation of org.springframework.orm.jpa.JpaVendorAdapter? As an example there exists the following class:
org.hibernate.ogm.jpa.impl.OgmEntityManagerFactory implements HibernateEntityManagerFactory
What auto configuration classes to disable?
What manual configuration is further required?
Leading on I got the following class suggested for controlling the persistence in Spring:
org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
I'm using Spring and Hibernate with an automatically generated database (for that I have set "hibernate.hbm2ddl.auto" to "update" in the JPA configuration properties).
I also have a class annotated #Configuration with a #PostConstruct method that is called on application startup after the database has been created or updated. This is where I setup the database with some default data if it's empty (first launch).
I would like to execute some custom native SQL queries at this moment. These queries won't return anything, they're just configuration stuff (like creating additional indexes or extensions).
Currently I'm stuck on creating a SessionFactory in order to create a new Hibernate Session. I've tried auto wiring it, but it doesn't work :
#Autowired
SessionFactory sessionFactory;
Gives me: Field sessionFactory in ... required a bean of type 'org.hibernate.SessionFactory' that could not be found.
I understand that I probably need to configure it elsewhere, but I don't know where. Several answers on SO use an xml configuration file, but I'm not using any configuration file so I can't do it that way.
Is there a way Spring can create the SessionFactory with the appropriate configuration ?
You don't even need to access SessionFactory. Please just put your scripts into a file src/main/resources/scripts/myscript.sql. You can then do the following with Spring:
#Component
public class Startup {
#Autowired
private DataSource dataSource;
#PostConstruct
public void runNativeSql() {
ClassPathResource resource = new ClassPathResource("scripts/myscript.sql");
try(Connection connection = dataSource.getConnection()) {
ScriptUtils.executeSqlScript(connection, resource);
} catch (SQLException | ScriptException e) {
//LOG
}
}
}
You can autowire the JPA EntityManager as:
#PersistenceContext
EntityManager entityManager;
If you really need a Hibernate Session and are using using JPA 2.1, the Session can be obtained from the EntityManager as:
entityManager.unwrap(Session.class);
I have a collection of DALS such as Transaction DAO, Billing DAO etc. Each of them have CRUD operations and I need to use that methods in my project using spring autowiring. In some of the projects that I have checked out, I saw that they are fetching the data inmemory by querying. However, I have to use the DAL's that have already been written and have all the CRUD operations.
For example:
#Autowired
TransactionDAO transactionDAO
#Autowired
BillingDAO billingDAO
#Test
public void testImplementSearchMethodForDAO() throws Exception{
TransactionVO transactionVO = getTransVO();
BillingVO billingVO = getBillingVO();
List<TransactionVO> VOList1 = transactionDAO.searchList(transactionVO);
List<BillingVO> VOList2 = billingDAO.searchList(billingVO);
assertThat(VOList1.size()).isEqualto(1));
assertThat(VOList1.size()).isEqualto(1));
}
(Assuming I added one VO value in each table).
If you need any more clarifications, I will be glad to provide you.
You can use setters and use the #Autowire annotation on the setters. Your test code must inject the DAOs using these setters. In you tests you build in mem: db instance and build a connection pool or datasource or whatever you need and then build the DAOs based of that.
Your setup() would something like this
BoneCPConfig config = new BoneCPConfig();
config.setJdbcUrl("jdbc:hsqldb:mem:test_common;shutDown=false");
config.setUsername("sa");
config.setPassword("");
JdbcTemplate dataSource = new BoneCPDataSource(config);
jdbcTemplate = new JdbcTemplate(dataSource);
//If you are using named queries
NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
//create necessary tables etc here.
setupDb(jdbcTemplate);
SomeBean anotherBean = new SomeBean();
YourDAO dao = new YourDAOImpl();
dao.setNamedJdbcTemplate(namedParameterJdbcTemplate);
dao.setSomeOtherBean(anotherBean);
//Mimic spring container if you implement InitialzingBean
dao.afterPropertiesSet();
Once all the dependencies are injected you run your tests as usual.
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.
What value should I place into <jta-data-source> of my persistence.xml?
In glassfish admin panel I created a datasource name "abcDS". In my jndi.properties (inside src/test/resources) I defined it like this:
[...]
abcDS=new://Resource?type=DataSource
abcDS.JdbcDriver=org.hsqldb.jdbcDriver
abcDS.JdbcUrl=jdbc:hsqldb:mem:testdb
abcDS.JtaManaged=true
[...]
What shall I place into persistence.xml? I've found a lot of variants in the Net, like: "jdbc/abcDS", "java:/abcDS", "abcDS". Which one is right? And is there some rule for this? I understand that it's related to JNDI, but...
I'm trying to create EMF in my unit test:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("abc");
This is what I'm getting in log:
[...]
SEVERE: Could not find datasource: abcDS javax.naming.NameNotFoundException:
Name "abcDS" not found.
at org.apache.openejb.core.ivm.naming.IvmContext.federate(IvmContext.java:193)
at org.apache.openejb.core.ivm.naming.IvmContext.lookup(IvmContext.java:150)
at org.apache.openejb.core.ivm.naming.ContextWrapper.lookup(ContextWrapper.java:115)
at javax.naming.InitialContext.lookup(InitialContext.java:392)
[...]
The problem is that Persistence.createEntityManagerFactory("abc") is the "do it yourself" API and doesn't take advantage of the Embedded EJB Container. You can get a container managed EntityManager in your test case very easily.
Just as with the related jndi/datasource question I recommend you check out the examples in the examples.zip. They're all designed to take the struggle out of getting started.
Here's a snippet from the testcase-injection example which shows how you can get an EntityManager and other things from the container for use in a test.
First, add an empty ejb-jar.xml or application-client.xml to your test to turn on scanning for your test code:
src/test/resources/META-INF/application-client.xml
Then, annotate your test case with #org.apache.openejb.api.LocalClient and use the standard JavaEE annotations for the actual injection.
#LocalClient
public class MoviesTest extends TestCase {
#EJB
private Movies movies;
#Resource
private UserTransaction userTransaction;
#PersistenceContext
private EntityManager entityManager;
public void setUp() throws Exception {
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
p.put("movieDatabase", "new://Resource?type=DataSource");
p.put("movieDatabase.JdbcDriver", "org.hsqldb.jdbcDriver");
p.put("movieDatabase.JdbcUrl", "jdbc:hsqldb:mem:moviedb");
InitialContext initialContext = new InitialContext(p);
// Here's the fun part
initialContext.bind("inject", this);
}
As movieDatabase is the only DataSource that we've setup, OpenEJB will automatically assign that DataSource to your persistence unit without the need to modify your persistence.xml. You can even leave the <jta-data-source> or <non-jta-data-source> empty and OpenEJB will still know what to do.
But for the sake of completeness, here's how this particular application has defined the persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="movie-unit">
<jta-data-source>movieDatabase</jta-data-source>
<non-jta-data-source>movieDatabaseUnmanaged</non-jta-data-source>
<class>org.superbiz.testinjection.Movie</class>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
</properties>
</persistence-unit>
</persistence>
Then the fun part, using it all together in tests
public void test() throws Exception {
userTransaction.begin();
try {
entityManager.persist(new Movie("Quentin Tarantino", "Reservoir Dogs", 1992));
entityManager.persist(new Movie("Joel Coen", "Fargo", 1996));
entityManager.persist(new Movie("Joel Coen", "The Big Lebowski", 1998));
List<Movie> list = movies.getMovies();
assertEquals("List.size()", 3, list.size());
for (Movie movie : list) {
movies.deleteMovie(movie);
}
assertEquals("Movies.getMovies()", 0, movies.getMovies().size());
} finally {
userTransaction.commit();
}
}