For regulatory and security reasons I had to split the logic of my Spring boot application into two tools: One for administration with limited number of tables and one for the "real" user application. Therefore I have two MySQL databases instances on the server version 5.7. While the user tool only accesses one database with dozens of tables, the admin tool needs to access entities in both databases.
The tools are both JavaFX and Spring Boot based. Because of this architectural setup I have three maven packages: One for the admin tool and all the admin related entities, services and alike, one for the user tool and all related entities, services etc. only relevant for this user tool, and the third with all the entities that both tools share.
When I run the user tool it generates the tables in the shared database and uses the hibernate ImprovedNamingStrategy based on the configuration in its application.properties file. Hence the columns have an underscore where appropriate.
In the first place the admin tool wouldn't create any database tables at all using spring.jpa.hibernate.ddl-auto, but I had to use spring.jpa.generate-ddl.
Now, when I run the admin tool I would expect that it only creates the tables in the admin database since this datasource is annotated as #Primary. But it also creates columns in the user database with mixed case. Hence I have columns named e.g. "email_address" and "emailAddress" in user database.
I wonder whether any properties are used with my approach? Any ideas how to do it properly?
Please find following some source..
application.properties :
# Employee database
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.jdbcUrl=jdbc:mysql://127.0.0.1/agiletunesdb?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&characterSetResults=utf-8
spring.datasource.username=YYY
spring.datasource.password=XXXXXX
# Account database
security.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
security.datasource.jdbcUrl=jdbc:mysql://127.0.0.1/authenticationdb?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&characterSetResults=utf-8
security.datasource.username=YYY
security.datasource.password=XXXXXX
# create db schema, values are none, validate, update, create, and create-drop.
#spring.jpa.hibernate.ddl-auto=create
spring.jpa.hibernate.ddl-auto=update
#spring.jpa.hibernate.ddl-auto=none
spring.jpa.generate-ddl=true
# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
Databases Configurations :
import javax.sql.DataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
#Configuration
public class MultipleDbConfiguration {
/*
* Persistence of admin database
*/
#Bean(name = "securityDB")
#Primary
#ConfigurationProperties(prefix="security.datasource")
public DataSource securityDataSource() {
return DataSourceBuilder.create().build();
}
/*
*
* Persistence of user database
*/
#Bean(name = "organizationDB")
#ConfigurationProperties(prefix="spring.datasource")
public DataSource organizationDataSource() {
return DataSourceBuilder.create().build();
}
}
The user database configuration
import java.util.HashMap;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "organizationEntityManagerFactory",
transactionManagerRef = "organizationTransactionManager",
basePackages = "com.agiletunes.domain.organization"
)
#EnableTransactionManagement
#PropertySources({ #PropertySource("classpath:application.properties") })
public class OrganizationConfig {
#Autowired
private Environment env; // Contains Properties Load by #PropertySources
#Bean(name = "organizationEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean organizationEntityManagerFactory(
EntityManagerFactoryBuilder builder, #Qualifier("organizationDB") DataSource dataSource) {
HashMap<String, Object> properties = new HashMap<>();
properties.put("spring.jpa.properties.hibernate.dialect", env.getProperty("spring.jpa.properties.hibernate.dialect"));
properties.put("spring.jpa.hibernate.ddl-auto", env.getProperty("spring.jpa.hibernate.ddl-auto"));
properties.put("spring.jpa.hibernate.naming-strategy", env.getProperty("spring.jpa.hibernate.naming-strategy"));
return builder
.dataSource(dataSource)
.packages("com.agiletunes.domain.organization")
.persistenceUnit("organizationPU")
.properties(properties)
.build();
}
#Bean(name="organizationTransactionManager")
public PlatformTransactionManager secondTransactionManager(#Qualifier("organizationEntityManagerFactory")
EntityManagerFactory organizationEntityManagerFactory) {
return new JpaTransactionManager(organizationEntityManagerFactory);
}
}
The trick was to use configuration classes which would have a
#PropertySources({ #PropertySource("classpath:application.properties") })
annotation. Then, in the method which creates the LocalContainerEntityManagerFactoryBean, you can pull and set values defined in your application.properties file with
properties.put("spring.jpa.hibernate.naming.physical-strategy", env.getProperty("spring.jpa.hibernate.naming.physical-strategy"));
Related
I have found the same issue here, but it did not help to asker, and it didnt help me too. So I try it again after three years :)
I made a Spring Boot app with MySQL database. All CRUD operations works fine, its tested by Postman. When I add Persistance.autoconfiguration.java it makes SQLException: Access denied for user 'XXX'#'localhost' (using password: NO). User XXX is my Windows user profile.
Persistence.configuration.java is placed in config package, it is placed in conferencedemo2, where are controller, repository, ect packages.
package com.cg.conferencedemo2.config;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
#Configuration
public class PersistenceConfiguration {
#Bean
public DataSource dataSource(){
DataSourceBuilder builder = DataSourceBuilder.create();
builder.url("jdbc:mysql://localhost:3306/conference_demo?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC");
System.out.println("My custom datasource hean has benn initialized and set");
return builder.build();
}
}
I have tryed to make this sql , but with no affect.
CREATE USER 'XXX'#'localhost' IDENTIFIED BY 'password' ----> 0 row(s) affected
GRANT ALL PRIVILEGES ON conference_demo.* TO 'XXX'#'localhost' ----->0 row(s) affected
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/conference_demo?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
Thank you for the any advice.
If you configure spring.datasource.* properties Spring will automatically inject an DataSource for you, you do not need the #Bean producer method using the builder.
In my case, I just remove the port:3306 in my spring.datasource.url. If you are using phpmyadmin as a database management tool.
Here's the link for the reference: DataSource Configuration
spring.datasource.url=jdbc:mysql://localhost/test
I am having trouble getting messages from a locally run ActiveMQ. I can produce them onto the queue and my PC also is registered as producer. However, another Spring App on the machine should be configured as a listener. So far it is not working. ActiveMQ is listening on the default ports.
My JMS config for the sender:
package at.dkepr.queueservice;
import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.jms.Queue;
#Configuration
public class JmsConfig {
#Bean
public Queue queue(){
return new ActiveMQQueue("indexing-queue");
}
}
And this is the consumer:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import at.dkepr.entity.UserSearchEntity;
#Component
#EnableJms
public class JmsConsumer {
private final Logger logger = LoggerFactory.getLogger(JmsConsumer.class);
#JmsListener(destination = "indexing-queue", containerFactory = "jmsListenerContainerFactory")
public void receive(UserSearchEntity user){
logger.info(user.getEmail());
}
}
In the application.propertiers I have added the necessary properties:
spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.user=admin
spring.activemq.password=admin
Also the UserSearchEntity implements Serializable.
To the best of my knowledge for this setup I should not even need a config for the consumer. Never the less, I added one.
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
#Configuration
#EnableJms
public class ConsumerConfig {
#Value("${spring.activemq.broker-url}")
private String brokerUrl;
#Bean
public ActiveMQConnectionFactory activeMQConnectionFactory() {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
activeMQConnectionFactory.setBrokerURL(brokerUrl);
return activeMQConnectionFactory;
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(activeMQConnectionFactory());
factory.setConcurrency("1-3");
return factory;
}
}
I am not getting any error logs. Unfortunately, it is simply doing nothing.
This is a screenshot from the ActiveMQ web console with the enqueued messages:
My consuming application was running when I took this screenshot, but the broker clearly does not recognize it since the "Number of Consumers" is 0.
Edit:
I just tried adding the Listener to the same Spring Application where the Producer is. Surprinsingly, the Listener connected fine. It seems like the problem lies in the different Spring Applications. However, i used the same application.properties for both Spring Apps. The Config File is the same too.
To everyone having the same problem:
For me it was a simple problem with folder structure. For some reason the Application.java for the consumer service was in a subfolder. After i moved the Application.java one folder up, the connection to the ActiveMQ worked.
My sample project is Maven based structure, all my application proeprties files under src/main/resources folder. Below is the complete sample code. I am not understanding why my code not able to find profiles properly unless I use #PropertySource annotation.
My actual doubt is: I've configured spring properties pretty well in application.properties file, but yet why it cannot find profile and their respective property files? Unless I am using #PropertySource annotation, iam not getting value for env.getProperty("mysql.url"). I mean Environment class not able to pick up values from profiles property files. WHY?
Iam getting error as below:
Jul 08, 2017 7:54:26 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext#300ffa5d: startup date [Sat Jul 08 19:54:26 IST 2017]; root of context hierarchy
helloBean
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'datasource' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:687)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1207)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1084)
at com.oreilly.datasource.Main2.main(Main2.java:15)
DatasourceConfig.java
package com.oreilly.datasource;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.core.env.Environment;
#Configuration
/*#PropertySource("classpath:/application.properties")
#PropertySource("classpath:/dev/application-dev.properties")
#PropertySource("classpath:/prod/application-prod.properties")*/
public class DatasourceConfig {
#Autowired
private Environment env;
#Bean(name="helloBean")
public String helloWorld() {
System.out.println("helloBean");
return "helloWorld....";
}
#Bean(name="datasource")
#Profile("dev")
public DataSource datasourceForDev(){
BasicDataSource dataSource = new BasicDataSource();
System.out.println(env.getProperty("mysql.url"));
return dataSource;
}
#Bean(name="datasource")
#Profile("prod")
public DataSource datasourceForProd(){
BasicDataSource dataSource = new BasicDataSource();
System.out.println(env.getProperty("mysql.url"));
return dataSource;
}
}
Main2.java
package com.oreilly.datasource;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DatasourceConfig.class);
DataSource dataSource = context.getBean("datasource", DataSource.class);
String helloBean = context.getBean("helloBean", String.class);
}
}
application.properties
spring.profiles.active=prod
spring.config.name=application
spring.config.location=classpath:/application.properties,classpath:/dev/application-dev.properties,classpath:/prod/application-dev.properties
Below is the project folder structure:
Please tell me what went wrong?
Spring is smart, it chooses the application-x.properties (where x is the environment) depending of the value assigned to spring.profiles.active in the application.properties, so you don't have to worry about register all the files in different #PropertySource annotations.
You can get more info here: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-profile-specific-properties
I suggest you remove all the #Profile annotations and let just one datasource that will be variable (depending on the seleced environment from application.properties). You may understand this with the example that i put at the end of this post.
If you want to define a mysql.url for a particular profile (lets say dev), you need to add the "mysql.url" in the application-dev.properties file, and then set the spring.profiles.active value to dev in application.properties.
Then, in your DatasourceConfig.java, you can perform something like this:
#Autowired
private Environment env;
//Takes the mysqlUrl from application-x.properties (where x is the value of spring.profiles.active that comes from application.properties)
#Value("${mysql.url}")
private String mysqlUrl;
#Bean(name="helloBean")...
#Bean(name="datasource")
public DataSource datasource() {
BasicDataSource dataSource = new BasicDataSource();
System.out.println(mySqlUrl); //This value is variable depending of the profile that you're pointing on.
return dataSource;
}
Please let me know it this is useful for you.
I have resolved my issue just by modifying as below:
#PropertySource("classpath:/${spring.profiles.active}/application-${spring.profiles.active}.properties")
Now I am able to pickup application-dev.properties (or) application-prod.properties dynamically.
Note: Environment class requires #PropertySource annotation, otherwise we get null for env.get('someproperty').
#Configuration
#PropertySource("classpath:application.properties")
public class DatasourceConfig {
....
}
put property file in same location as application.property and follow the naming convention application-{profile}.properties like application-dev.properties, application-prod.properties.
'I want properties file to be picked up automatically based upon profile I declared in application.properties.'
:
Run you application with -Dspring.profiles.active=dev/prod . Spring load 1)application.property , 2)pplication-dev/prod.properties file with overides value from application.property
I wanted to know if there is any way in Spring Boot to read property values from properties file by using Dynamic Keys. I know properties can be put in application.properties and can be read using #Value("propertyKey") But my keys are going to be dynamic.
I know about #PropertySource to read property values and I can construct my keys dynamically. So is there any way that is provided by Spring Boot?
you can use:
#Autowired
private Environment env;
and then load property from code:
env.getProperty("your.property")
1- Register a Properties File via Java Annotations.
#Configuration
#PropertySource("classpath:test.properties")
public class PropertiesJavaConfig {
}
2- Dynamically select the right file at runtime.
#PropertySource({
"classpath:persistence-${envTarget:DB}.properties"
})
In case you are reading from the application.properties, you just define the environment spring autowired variable as specified by freakman (org.springframework.core.env.Environment). But if you are using a new properties file specific for certain properties, you can use the following code:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
#Configuration
#PropertySource("classpath:filename.properties")
public class CustomConfig {
#Autowired
Environment env;
public String getProperty(String keyName) {
return env.getProperty(keyName);
}
}
In a Spring Boot application I'm trying to setup multiple database connections. I've started building the primary datasource, but I'm getting the following error on the mySqlEntityManagerFactory method.
Could not autowire. no beans of EntityManagerFactoryBuilder
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.PersistenceContext;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
#Configuration
#Transactional
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = "digital.sheppard.dao",
entityManagerFactoryRef = "entityManager",
transactionManagerRef = "transactionManager")
public class PrimaryDBConfig {
#Bean(name="dataSource")
#Primary
#ConfigurationProperties(prefix = "primary.datasource.mysql")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#PersistenceContext(unitName = "primary")
#Primary
#Bean(name = "entityManager")
public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder.dataSource(mysqlDataSource()).persistenceUnit("primary").properties(jpaProperties())
.packages("digital.sheppard.model").build();
}
private Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<String, Object>();
props.put("hibernte.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
props.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return props;
}
}
How would I autowire the EntityManagerFactoryBuilder?
I'm trying to follow the code on this blog https://raymondhlee.wordpress.com/2015/10/31/configuring-multiple-jpa-entity-managers-in-spring-boot/
Here's the main application class if it's helpful
#Configuration
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
#ComponentScan
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
For me the reason was a wrong import, so I advise everybody to look at the often overlooked import section of your configuration class. There is a Hibernate class named the same as the springframework one. Of course you should choose the spring one...
I think you should remove this code
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
The exception is due to public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
which expects a bean of EntityManagerFactoryBuilder.
I checked the reference link, I am not sure if that code will work.
Typically, one creates an instance of LocalContainerEntityManagerFactoryBean and initializes it as per need. In your case you can do
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(mysqlDataSource());
em.setPersistenceUnitName("primary");
em.setPackagesToScan(new String[] { "digital.sheppard.model" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(jpaProperties());
The persistence unit name should be same as defined in persistence.xml, though the file is now optional when using Spring JPA.
For a non spring version check out https://stackoverflow.com/a/26814642/776548
Also
since you are initializing EntityManagerFactory by yourself, we will have to exclude DataSourceAutoConfiguration.class.
#Primary is only required if you want multiple datasources. If you have only one, consider removing the annotation, and add it when you need to have multiple data sources
Change parameter name builder to entityManagerFactoryBuilder to inject bean present in JpaBaseConfiguration.class
Have you tried to remove your exclusion of 'DataSourceAutoConfiguration' ?
Using '#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})' prevent a lot of beans from beeing created.
If you got a problem when using a datasource and adding this is your solution, maybe it's not the right one.
Know that spring boot detect the presence of certain classes in the classpath.
If you're using maven, it's reading all classes from all dependencies.
So consider let this DataSourceAutoConfiguration.class running;
cheers
It could be, notice that just could be, your main class is not at the top of the "class tree". Spring needs to scan all classes that are a child (according to package convention) starting from the main class.
Maybe you would read https://www.baeldung.com/spring-component-scanning
If your classes aren't been read by spring scan, they will never be into spring context.
Couple of possibilities :
You need to add the #EnableJpaRepositories(basePackages = {"your.pkg.here"}) to the Application . This tells Spring Data to look for your repository classes under the specified package.
For me , removing #Primary on multiple Data sources, which was added by mistake fixed the issue
it was caused by your ide software,set up these options