I'm trying to get started with using Guice Persist and JPA, which recommends using configuration via persistence.xml. Coming from a native Hibernate background where configuration was obtained programmatically, is there a simple way to configure a JpaPersistModule without a persistence.xml file, or will a rump persistence.xml always have to exist?
If no such option exists, it might be the case where I might have to play around with PersistenceProvider (assuming the "default" parses persistence.xml somehow). Any tutorials on working with the JPA SPI?
There is no need for persistence.xml if you are using a Spring version higher than 3.1 and you have already defined your entities classes.
#Configuration
#ComponentScan(basePackages = { "com.demoJPA.model" })
#EnableTransactionManagement
public class DemoJPAConfig {
#Bean
public DataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("org.gjt.mm.mysql.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/cimto");
dataSource.setUser("user");
dataSource.setPassword("pass");
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws PropertyVetoException {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setJpaVendorAdapter(vendorAdapter());
em.setPersistenceUnitName("cimtoPU");
em.setJpaPropertyMap(getJpaProperties());
return em;
}
public Map<String, ?> getJpaProperties() {
return new HashMap<String, Object>();
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
public JpaVendorAdapter vendorAdapter() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.MYSQL);
vendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQL5Dialect");
vendorAdapter.setShowSql(true);
return vendorAdapter;
}
}
Note: com.demoJPA.model package must contain your entities classes.
Assuming that you have a PersistenceProvider implementation (e.g. Hibernate), you can use the PersistenceProvider#createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) method to bootstrap an EntityManagerFactory without needing a persistence.xml.
However, it's annoying that you have to implement the PersistenceUnitInfo interface, so you are better off using Spring or Hibernate which both support bootstrapping JPA without a persistence.xml file:
this.nativeEntityManagerFactory = provider.createContainerEntityManagerFactory(
this.persistenceUnitInfo,
getJpaPropertyMap()
);
Where the PersistenceUnitInfo is implemented by the Spring-specific MutablePersistenceUnitInfo class.
Depending on what you want to achieve and in what context (ApplicationServer vs CLI, CMT transactions vs EntityTransactions), it may be possible to use JPA without a persistence.xml. I did this in a CLI Java application, where I had different databases with the same structure. For that I constructed the EntityManagerFactory manually.
PS: The config file is there to make your life easier, so if you can, just use it.
Related
Scenario:
I have an application extending another application and so it has 2 DataSource and 2 EntityManagerFactory in it. The first EntityManagerFactory is created for the original application, and the second one is what is created and used in my extension. The application being extended uses many of its own jars and I am not looking at modifying any of the code in the original application's jars.
Bean definitions:
#Bean("em1")
#Primary
#PersistenceContext(unitName = "pc1")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
#Qualifier("ds1") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
retVal.setDataSource(myDataSource);
retVal.setJpaProperties(Properties.getJpaProperties());
return retVal;
}
#Bean(name = "em2")
#PersistenceContext(unitName = "pc2")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
#Qualifier("ds2") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource);
emf.setPersistenceXmlLocation("classpath:persistence/persistence.xml");
return emf;
}
Issue:
I can successfully get an EntityManager from the second one in my classes by using
#PersistenceContext(unitName = "pc2")
protected EntityManager entityManager;
However when the original application tries to get an EntityManager in one of its libraries by
#Autowired
private EntityManager myEntityManager;
it throws a NoUniqueBeanDefinitionException saying it found 2 beans of type EntityManager
Question:
How can I make my #Primary EntityManagerFactory create an EntityManager that is also used by default? Or what other solution can I do to fix these conflicting beans without modifying the original application?
I'm developing a standalone application with following technology stack:
Spring Boot version 2.1.0.RELEASE
Oracle12c System with driver ojdbc6 (11.2.0.3)
Apache Camel
JPA for the main datasource
JDBC for a secondary datasource (read only)
The JPA datasource is the primary datasource where the application itself is connected to and write data to. The JDBC is an additional datasource to read data from a database with another purpose.
During runtime I encounter the following issue:
I poll/select a JPA Entity from the primary datasource and do some processing. This processing includes running a select query on the secondary datasource via a jdbc template. Now if the execution of the query throws an exception I am able to catch it and want to update a status field on the JPA Entity and write it to the datasource.
I've already read that Oracle tries to do a rollback if a SQLException occurrs. The issue is that my JPA datasource is unable to commit the changes to the Entity I do when the JDBC query fails.
It seems to me like the two datasources/transaction managers are not completely independent of each other and that an exception in the secondary datasource causes errors in the primary datasource during commiting changes.
Is this even possible? If yes, how can I configure two independent transaction managers?
EDIT:
I have already tried to annotate the respective methods and classes with #Transactional(noRollbackFor = Exception.class) but this does not solve the problem.
Here are the two Datasource configurations:
ApplicationDatasourceConfig (JPA)
#Configuration
#EnableJpaRepositories(basePackages = "foo.bar.repository.jpa",
entityManagerFactoryRef = "applicationEntityManagerFactory",
transactionManagerRef = "applicationTransactionManager")
#ConfigurationProperties(prefix = "spring.datasource.hikari")
#EnableTransactionManagement
public class ApplicationDatasourceConfig extends HikariConfig{
#Bean("applicationDatasource")
#Primary
public DataSource applicationDataSource(){
return new HikariDataSource(this);
}
#Bean("applicationDatasourceProperties")
#Primary
public DataSourceProperties dataSourceProperties(){
return new DataSourceProperties();
}
#Bean("applicationEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean applicationEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("applicationDatasource") DataSource dataSource){
return builder
.dataSource(dataSource)
.packages("foo.bar.entity")
.build();
}
#Bean("applicationTransactionManager")
#Primary
public PlatformTransactionManager applicationTransactionManager(#Qualifier("applicationEntityManagerFactory")EntityManagerFactory entityManagerFactory){
return new JpaTransactionManager(entityManagerFactory);
}
}
SecondaryDatasourceConfig (JDBC)
#Configuration
#ConfigurationProperties(prefix = "secondary.datasource")
#EnableTransactionManagement
public class SecondaryDatasourceConfig {
#Bean("secondaryDatasource")
public DataSource secondaryDataSource(){
return secondaryDataSourceProperties().initializeDataSourceBuilder().build();
}
#Bean
public DataSourceProperties secondaryDataSourceProperties(){
return new DataSourceProperties();
}
#Bean("secondaryTransactionManager")
public PlatformTransactionManager secondaryTransactionManager(#Qualifier("secondaryDatasource") DataSource secondaryDataSource){
return new DataSourceTransactionManager(secondaryDataSource);
}
}
I have gone through the spring data jpa reference documentation
to configure a datasource in spring boot,and with LocalcontainerEntityManagerFactoryBean and transactionManager..etc,but run it
with error
but I want configure a datasource of mysql,single datasource.
this is configuration class code:
#Configuration
#EnableJpaRepositories
#EnableTransactionManagement
public class DataSourceConfig {
#Bean
#ConfigurationProperties(prefix="oneslide.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendor=new HibernateJpaVendorAdapter();
vendor.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory=new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendor);
factory.setPackagesToScan("com.oneslide.multiDataSource.domain");
factory.setDataSource(dataSource());
return factory;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager=new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
I don't want user DatasourceBuilder.create.url().password().. something chain invocation like that,I just want to congiure my sql connection metadata in
application.properties with oneslide.datasource namespce.And try to use the LocalContainerEntityManagerFactory Bean,not with tutorial's way in which they
use spring.datasource.* property.
but when i run it datasource debug info is null,there it is digest of exception log:
Invocation of init method failed; nested exception is
org.hibernate.service.spi.ServiceException: Unable to create requested
service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
: Access to DialectResolutionInfo cannot be null when
'hibernate.dialect' not set
Help me thanks a lot.
I totally misunderstand spring boot,Maybe.....it autoconfigure all bean,like
LocalContainerEntityManagerfactoryBean!so to use multidatasource,I just need to configure a datasource only,Right???
Your annotation #ConfigurationProperties(prefix="oneslide.datasource") is asking Sprint to get the info from the external configuration and bind it with the bean you are annotating, i.e. the result produced by the method.
BUT the annotated bean MUST have the properties to receive the configuration values. I.e. it has to have fields and inner objects that replicate the structure of the configurations you are passing (and the setters too).
As example if your config contains something like below:
oneslide.datasource.url = some_url
oneslide.datasource.user = usr
oneslide.datasource.password = pw
oneslide.datasource.special.detail = whatever
The bean you build should have fields "url", "user" and "password" AND an object "special" with a field "detail", so that Spring can set the values. Simplifying something along Y = X.getSpecial(); Y.setDetail() (with null recognition and object creation too, I think to remember).
If you do nothing... Spring behind the scene will create a DataSourceProperties bean (that unsurprisingly contains the fields normally used to set up a datasource with the config info under "spring.datasource").
You can get hold of this bean by defining your own bean that gets it as a parameter, like below:
public <whatever> getTheD_S_Properties(DataSourceProperties myDataSourceValesFromConfig) {
...do something with the bean you got,
that contains the values from your config...
}
The most common operation, in this case is to just build the datasource yourself, with some logic beyond just assigning the values form the config.
If you need to do nothing special, then let Spring to build the datasource too.
Just sit back and enjoy ! :)
I've been successful using using the Accessing Data With JPA tutorial for Spring. I've gotten a CrudRepository of my own to work automatically by just configuring a specific DataSource #Bean, and the internal connections between these are managed by Spring Data (or Spring Boot, it's hard to tell which).
However, I can't figure out how to get that automated plumbing to handle a second DataSource #Bean. Injecting a second one causes the autoconfiguration classes to explode during startup.
Any thoughts as to how to do this? The searches I've done for this resulted in articles discussing multiple homogeneous DataSources for load balancing or other purposes, which is really not what I need. I have multiple databases with completely separate content that I need to pull into this app and I'd really like to avoid having to replicate all that automated configuration just because a second database entered the mix.
I'm hoping this is simple, but I'm fearful that it's an unsupported edge case in the autoconfiguration.
You can create two datasources and entitymanagers, one bean of them mark as #Primary
#Configuration
#EnableJpaRepositories(basePackages = "io.eddumelendez.springdatajpa.repository1")
public class FirstConfiguration {
#ConfigurationProperties(prefix = "datasource.postgres")
#Bean
#Primary
public DataSource postgresDataSource() {
return DataSourceBuilder.create().
build();
}
#Bean(name = "entityManagerFactory")
#Primary
public LocalContainerEntityManagerFactoryBean emf1(EntityManagerFactoryBuilder builder){
return builder
.dataSource(postgresDataSource())
.packages("io.eddumelendez.springdatajpa.domain1")
.persistenceUnit("users")
.build();
}
}
Configuration for another datasource:
#Configuration
#EnableJpaRepositories(basePackages = "io.eddumelendez.springdatajpa.repository2", entityManagerFactoryRef = "emf2")
public class SecondConfiguration {
#Bean
#ConfigurationProperties(prefix = "datasource.mysql")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean emf2(EntityManagerFactoryBuilder builder){
return builder
.dataSource(mysqlDataSource())
.packages("io.eddumelendez.springdatajpa.domain2")
.persistenceUnit("customers")
.build();
}
}
Your application.properties should looks like this:
datasource.mysql.url=jdbc:mysql://localhost:3306/mysql_demo
datasource.mysql.username=root
datasource.mysql.password=root
datasource.postgres.url=jdbc:postgresql://localhost:5432/postgres_demo
datasource.postgres.username=postgres
datasource.postgres.password=postgres
I'm using Oracle Spatial, and I have a table with an SDO_GEOMETRY field.
The table is mapped to a JPA entity. I want to have the SDO_GEOMETRY field mapped to a java oracle.spatial.geometry.JGeometry type.
I figured I should use a JPA Converter and to convert to and from java.sql.Struct (or maybe oracle.sql.STRUCT).
The problem is the JGeometry method that converts to Struct, JGeometry.storeJS(Connection conn, JGeometry geom), wants the jdbc connection as a parameter.
The spring EntityManagerFactory is configured with the persistence unit name, the persistence unit contains the data source jndi name, and the data source is defined in tomcat, as a connection pool.
Any idea on how I can get the Connection in the converter ?
This what I want to achieve:
#Converter(autoApply = true)
public class GeometryConverter implements AttributeConverter<JGeometry, Struct> {
#Override
public Struct convertToDatabaseColumn(JGeometry geometry) {
// How to get this connection ?
return JGeometry.storeJS(connection, geometry);
}
#Override
public JGeometry convertToEntityAttribute(Struct struct) {
try {
return JGeometry.loadJS(struct);
} catch (SQLException e) {
throw new RuntimeException("Failed to convert geometry", e);
}
}
}
I am using Spring 4, spring-data-jpa 1.6, Hibernate 4, Tomcat 8, Oracle 12c.
Updated with more info:
Spring configuration:
#Configuration
#EnableJpaRepositories("com.package.repository")
#EnableTransactionManagement
#ComponentScan("com.package")
public class SpringConfig {
#Bean(name = "entityManagerFactory", destroyMethod = "destroy")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setPersistenceUnitName("persistence-unit");
return emf;
}
#Bean(name = "transactionManager")
public JpaTransactionManager getTransactionManager() {
return new JpaTransactionManager();
}
}
If you use spring, and you need to use both JPA and JDBC, you should :
construct a datasource bean and make connection pooling there (or get if from jndi(*))
inject that datasource in one on the spring helpers for building the EntityManagerFactory (such as LocalContainerEntityManagerFactoryBean)
inject that datasource in any bean where you want to do direct JDBC
That way you can use JPA for your normal DAO, and still have access to JDBC in special parts - without a too strong dependance of the internals of your JPA provider.
EDIT:
(*) If your datasource is defined by a jndi name, all is fine. Expose it as a bean (ref)
If using Spring's XML schema based configuration, setup in the Spring context like this:
<xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.2.xsd">
...
<jee:jndi-lookup id="dbDataSource"
jndi-name="jdbc/DatabaseName"
expected-type="javax.sql.DataSource" />
Alternatively, setup using simple bean configuration like this:
<bean id="dbDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/DatabaseName"/>
</bean>
As you are using a JpaTransactionManager, there will not be any problem because as specified in spring javadoc This transaction manager also supports direct DataSource access within a transaction (i.e. plain JDBC code working with the same DataSource). This allows for mixing services which access JPA and services which use plain JDBC (without being aware of JPA)! provided you get your Connection through DataSourceUtils.getConnection(javax.sql.DataSource)
EDIT2 :
Ok now the only problem is how to access a singleton bean from a non bean object. A simple way to solve it is to create a holder singleton bean with a static method.
#Bean
public class DataSourceHolder implements InitializingBean {
private DataSource dataSource;
private static DataSourceHolder instance;
public static DataSource getDataSource() {
return instance.dataSource;
}
#Autowired
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
#Override
public void afterPropertiesSet() throws Exception {
DataSourceHolder.instance = this;
}
}
Then in any object, be it a bean or not, you can use
DataSource ds = DataSourceHolder.getDataSource();
Connection con = DataSourceUtils.getConnection(ds);
That would be tricky. Purely from the jpa api. You will have to dig into the specific provider implementation and get hold of the DataSource object or the PersistenceUnitInfo object.
From here you can get hold of the Connection object.
Now depending on which environment you are working. If you are on an JavaEE environment, and you inject EntityManager or its EntityManagerFactory, there is no guarantee that the return instance is an instance of the provider own implementation as this may just be a proxy that implements the interface, and hence no relation to the provider's own implementation.
On JSE environment, since you are the one creating the EntityManagerFactory from Persistence.createEntityManagerFactory(), you could tweak the provider in order to get the Connection.