Hibernate 4.3 SchemaUpdate with JPA - java

I am trying to write a Liquibase changeset generator based on SchemaUpdate class provided by Hibernate.
The goal is to have the benefits of Hibernate update scripts and the possibility for developpers to tweak them as needed.
When done, the SQL will be intergated in a Liquibase SQL file.
This seemed very simple at the beginning, by instanciating a SchemaUpdate class and launch the execute method.
But the code does anything. I am trying to initialize Hibernate from a JPA persistence.xml file and I think that's the problem.
For the moment I have manually set mysql properties, but Hibernate doesn't find the entity classes.
What's the best way to get a configuration (needed by the SchemaUpdate class) from JPA configuration ?
I've searched a solution for a while now, but all of them use EJB3Configuration class which doesn't exist anymore in Hibernate 4.3.
Thaks for Help !
For example, I am doing something like this :
final File persistenceFile = new File("src/main/resources/META-INF/persistence.xml");
final Configuration cfg = new Configuration();
if ( ! persistenceFile.exists()) {
throw new FileNotFoundException(persistenceFile.getAbsolutePath() + " not found");
}
final List<String> jpaConfiguration = Files.readLines(persistenceFile, Charsets.UTF_8);
for (final String line : jpaConfiguration) {
if (line.contains("property")) {
final String[] props = line.split("\\\"");
cfg.setProperty(props[1], props[3]);
}
}
final SchemaUpdate updater = new SchemaUpdate(cfg);
updater.setOutputFile("target/script.sql");
updater.execute(true, false);

This solution has a drawback. You must also include the 'package-info.java', if there is one. There is a simpler solution (at least for now).
HibernatePersistenceProvider can generate EntityManagerFactoryBuilder and
EntityManagerFactoryBuilderImpl has a function to give a Configuration back. Unfortunately, this function is protected, so must HibernatePersistenceProvider be derived.
private class MyHibernatePersistence extends HibernatePersistenceProvider {
#Override
public EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String persistenceUnitName, Map properties) {
return super.getEntityManagerFactoryBuilderOrNull(persistenceUnitName, properties);
}
}
This can be easily created a Configuration, which takes into account all the data, which are also used for a EntityManager.
public Configuration getConfiguration(String persistenceUnitName, Map<String, Object> properties) {
MyHibernatePersistence hpp = new MyHibernatePersistence();
EntityManagerFactoryBuilder emfb = hpp.getEntityManagerFactoryBuilderOrNull(persistenceUnitName, properties));
if (emfb instanceof EntityManagerFactoryBuilderImpl) {
emfb.build();
return ((EntityManagerFactoryBuilderImpl) emfb).getHibernateConfiguration();
}
return null;
}

Related

Specify hibernate.multi_tenant_connection_provider without using reflection

Currently I am specifying the hibernate multi tenant connection provider and resolver in a properties file using this.
hibernate.multi_tenant_connection_provider: com.app.MapMultiTenantConnectionProvider
hibernate.tenant_identifier_resolver: com.app.MultiTenantCurrentTenantIdentifierResolver
Hibernate is using reflection to load these classes. The problem is that I need these classes to have access to certain variables. E.g. The DropWizard config file and users organisation to know what the database URL is and the tenant id. Currently I'm having to make variables static so that the provider has access to it.
The tutorials like this have all the required info specified in the properties file while I need mine to be dynamic depending on the currently connected user.
Here is an example of the kind of thing I'm having to do with static variables. Overall it makes the code quite messy.
public class MapMultiTenantConnectionProvider extends AbstractMultiTenantConnectionProvider {
private static Logger log = LoggerFactory.getLogger(MapMultiTenantConnectionProvider.class);
private Map<String, ConnectionProvider> connectionProviderMap = new HashMap<>();
public MapMultiTenantConnectionProvider() throws IOException {
}
#Override
protected ConnectionProvider getAnyConnectionProvider() {
return getConnectionProviderForTenant("chorus");
}
#Override
protected ConnectionProvider selectConnectionProvider(final String tenantIdentifier) {
return getConnectionProviderForTenant(tenantIdentifier);
}
private ConnectionProvider getConnectionProviderForTenant(final String tenantId) {
final ConnectionProvider connectionProvider;
if (!connectionProviderMap.containsKey(tenantId)) {
// Access a static variable here that contains the database URL, username, etc
final MyConfig config = MyApp.CONFIGURATION;
final Properties properties = new Properties();
properties.setProperty("hibernate.connection.url", config.connectionUrl);
properties.setProperty("hibernate.connection.username", config.username);
properties.setProperty("hibernate.connection.password", config.password);
properties.setProperty("hibernate.dialect", config.databaseConfig.getHibernateDialect());
final DriverManagerConnectionProviderImpl newConnectionProvider = new DriverManagerConnectionProviderImpl();
newConnectionProvider.configure(properties);
this.connectionProviderMap.put(tenantId, newConnectionProvider);
connectionProvider = newConnectionProvider;
} else {
connectionProvider = connectionProviderMap.get(tenantId);
}
return connectionProvider;
}
}
I would like to create an instance and pass in the configuration (or anything else needed) like this.
new MapMultiTenantConnectionProvider(configuration)
Is it possible to specify the provider by creating an instance of it instead of defining it in a properties file?
You could implement the ServiceRegistryAwareService interface to get access to the Hibernate ServiceRegistry which provides you access to almost all Hibernate configurations. I don't know how you'd normally access this dropwizard configuration, but in case it is available through a managed bean, you could access the ManagedBeanRegistry and access the bean that provides this information. Other than that, there is not much you can do. Please note though, that you current implementation is not thread safe. You should be using a ConcurrentHashMap and use putIfAbsent, or even better, computeIfAbsent.

Setting up Mongo Extension for Axon Framework on spring boot

So at first I added a properties file with:
spring.data.mongodb.uri=mongodb://axon:axon#aurl:27017/axonframework
which works but I was forced to use axonframework as db name because it is what was created in my mongo db.
Now controlling the db name and other details isn't an option in this case, so I went and checked around and found the following:
#configuration
public class AxonConfiguration {
#Value("${mongo.host:127.0.0.1}")
private String mongoHost;
#Value("${mongo.port:27017}")
private int mongoPort;
#Value("${mongo.db:test}")
private String mongoDB;
#Bean
public MongoSagaStore sagaStore() {
return new MongoSagaStore(axonMongoTemplate());
}
#Bean
public TokenStore tokenStore(Serializer serializer) {
return new MongoTokenStore(axonMongoTemplate(), serializer);
}
#Bean
public EventStorageEngine eventStorageEngine(Serializer serializer) {
return new MongoEventStorageEngine(serializer, null, axonMongoTemplate(), new DocumentPerEventStorageStrategy());
}
#Bean
public MongoTemplate axonMongoTemplate() {
return new DefaultMongoTemplate(mongo(), mongoDB);
}
#Bean
public MongoClient mongo() {
MongoFactory mongoFactory = new MongoFactory();
mongoFactory.setMongoAddresses(Collections.singletonList(new ServerAddress(mongoHost, mongoPort)));
return mongoFactory.createMongo();
}
}
Now apparently this worked for people but what I'm not being able to get right is how am I supposed to set the username and password?
I'm using axon 4.1, axonframework.extensions.mongo 4.1
The snippet of code you share does not correspond with Axon Framework release 4.x or Axon Mongo Extension release 4.x. The shift from version 3 to 4 has replaced almost all constructors of the infrastructure components in favor of the Builder pattern.
As such, you should not be able to do new MongoEventStorageEngine(...), but instead should do:
MongoEventStorageEngine.builder().mongoTemplate(axonMongoTemplate).build()
If you're still able to use the constructor, I assume you still have Axon 3 somewhere on the class path!
Regarding the Mongo specifics, I'd trust #PolishCivil's statement by the way.
Hope this helps!
This issue is not really related to the axon itself but more likely to the spring configuration of the mongo client instance since the usage of mongo is just an extension over the axon framework.
AFAIK it's
spring.data.mongodb.password
and
spring.data.mongodb.username
Also theres one thing in the code you should consider changing
return new DefaultMongoTemplate(mongo(), mongoDB);
You call the method that is specified as a bean, so instead in spring you should just wire it to your method parameter like so :
public MongoTemplate axonMongoTemplate(MongoClient client) {
return new DefaultMongoTemplate(client, mongoDB);
}

Read qualified yaml file with prefix without using annotations or xml

My use case is a bit oddball but basically, I'd like to read a portion of a yaml file and map it to the appropriate java object in a spring application. This is a pretty common and trivial operation in spring (just use #ConfigurationProperties ).
However, in my case, I'd like to accomplish this reading earlier in the lifecycle i.e. by the time the BeanFactoryPostProcessor hooks in - in order to use the instructions specified in yml to dynamically create a number of beans.
I can get this working with application.properties but not with application.yml
I was hoping to use yml in order leverage mapping part of the yml to POJO and also utilize hierarchical mapping files and data structures (lists, maps etc).
Here's an example of how to read application.properties. https://blog.pchudzik.com/201705/dynamic-beans/
I set up a simple skeleton project at https://github.com/balamuru/yaml-loader to try out different techniques.
Any ideas ?
#Component
#EnableConfigurationProperties(SampleDataConfig.class)
class ConfigurableBeanFactory implements BeanFactoryPostProcessor, InitializingBean {
private List<String> beanInstances = new ArrayList<>();
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
final BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
Map<String, SampleDataConfig> beans = beanFactory.getBeansOfType(SampleDataConfig.class);
System.err.println("");
beanInstances.forEach(instance -> {
registry.registerBeanDefinition(instance, BeanDefinitionBuilder
.rootBeanDefinition(SampleDataConfig.class)
.addConstructorArgValue(instance)
.getBeanDefinition());
});
}
#Override
public void afterPropertiesSet() throws Exception {
// this.beanInstances = asList(PropertiesLoaderUtils
// .loadProperties(new ClassPathResource("/application.properties"))
// .getProperty("dynamic-beans.instances", "")
// .split(","));
/**
* Rather than reading from application.properties,
* I would like to be able to load up the relevant prefix qualified segments (com.foo.bar.stuff) mapping to my POJO (SampleDataConfig,class)
* loaded from application.yml
*/
}
}
Internally, spring uses the following mechanism, but I was hoping there is an easier way to leverage this without re-inventing the spring :)
public class ConfigurationPropertiesBindingPostProcessor ...{
.
.
private void postProcessBeforeInitialization(Object bean, String beanName,
ConfigurationProperties annotation) {
Object target = bean;
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
target);
factory.setPropertySources(this.propertySources);
factory.setValidator(determineValidator(bean));
// If no explicit conversion service is provided we add one so that (at least)
// comma-separated arrays of convertibles can be bound automatically
factory.setConversionService(this.conversionService == null
? getDefaultConversionService() : this.conversionService);
if (annotation != null) {
factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
factory.setExceptionIfInvalid(annotation.exceptionIfInvalid());
factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties());
if (StringUtils.hasLength(annotation.prefix())) {
factory.setTargetName(annotation.prefix()); //====> use annotation prefix
}
}
try {
factory.bindPropertiesToTarget(); //===> bind properties
}
Thanks
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
yaml.setResources(new ClassPathResource("application.yml"));
configProperty = yaml.getObject();
Set<Object> keys = configProperty.keySet();
Below is my YAML configuration, which looks like:
template:
config:
broker-urls:
- tcp://127.0.0.1:61616
- tcp://127.0.0.1:61617
- tcp://127.0.0.1:61618
qeues:
- Test
- Demo
- Qeue3
After applying above code you will get converted properties like below:
template.config.broker-urls[0]=tcp://127.0.0.1:61616
template.config.broker-urls[1]=tcp://127.0.0.1:61617
template.config.broker-urls[1]=tcp://127.0.0.1:61618
template.config.qeues[0]=Test
template.config.qeues[1]=Demo
template.config.qeues[1]=Qeue3

Can I manually load #ConfigurationProperties without the Spring AppContext?

Is there any way to load a class marked with #ConfigurationProperties without using a Spring Context directly? Basically I want to reuse all the smart logic that Spring does but for a bean I manually instantiate outside of the Spring lifecycle.
I have a bean that loads happily in Spring (Boot) and I can inject this into my other Service beans:
#ConfigurationProperties(prefix="my")
public class MySettings {
String property1;
File property2;
}
See the spring docco for more info http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-external-config-command-line-args
But now I need to access this bean from a class that is created outside of Spring (by Hibernate). The class is created so early in the app startup process that Spring Boot has not yet made the application context available through the classic lookup helper methods or roll-my-own static references.
So I instead want to do something like:
MySettings mySettings = new MySettings();
SpringPropertyLoadingMagicClass loader = new SpringPropertyLoadingMagicClass();
loader.populatePropertyValues(mySettings);
And have MySettings end up with all its values loaded, from the command line, system properties, app.properties, etc. Is there some class in Spring that does something like this or is it all too interwoven with the application context?
Obviously I could just load the Properties file myself, but I really want to keep Spring Boot's logic around using command line variables (e.g. --my.property1=xxx), or system variables, or application.properties or even a yaml file, as well as its logic around relaxed binding and type conversion (e.g. property2 is a File) so that it all works exactly the same as when used in the Spring context.
Possible or pipe dream?
Thanks for your help!
I had the same "issue".
Here is how I solved it in SpringBoot version 1.3.xxx and 1.4.1.
Let's say we have the following yaml configuration file:
foo:
apis:
-
name: Happy Api
path: /happyApi.json?v=bar
-
name: Grumpy Api
path: /grumpyApi.json?v=grrr
and we have the following ConfigurationProperties:
#ConfigurationProperties(prefix = "foo")
public class ApisProperties {
private List<ApiPath> apis = Lists.newArrayList();
public ApisProperties() {
}
public List<ApiPath> getApis() {
return apis;
}
public static class ApiPath {
private String name;
private String path;
public String getName() {
return name;
}
public void setName(final String aName) {
name = aName;
}
public String getPath() {
return path;
}
public void setPath(final String aPath) {
path = aPath;
}
}
}
Then, to do the "magic" things of Spring Boot programmatically (e.g. loading some properties in a static method), you can do:
private static ApisProperties apiProperties() {
try {
ClassPathResource resource;
resource = new ClassPathResource("/config/application.yml");
YamlPropertiesFactoryBean factoryBean;
factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setSingleton(true); // optional depends on your use-case
factoryBean.setResources(resource);
Properties properties;
properties = factoryBean.getObject();
MutablePropertySources propertySources;
propertySources = new MutablePropertySources();
propertySources.addLast(new PropertiesPropertySource("apis", properties));
ApisProperties apisProperties;
apisProperties = new ApisProperties();
PropertiesConfigurationFactory<ApisProperties> configurationFactory;
configurationFactory = new PropertiesConfigurationFactory<>(apisProperties);
configurationFactory.setPropertySources(propertySources);
configurationFactory.setTargetName("foo"); // it's the same prefix as the one defined in the #ConfigurationProperties
configurationFactory.bindPropertiesToTarget();
return apisProperties; // apiProperties are fed with the values defined in the application.yaml
} catch (BindException e) {
throw new IllegalArgumentException(e);
}
}
Here's an update to ctranxuan's answer for Spring Boot 2.x. In our situation, we avoid spinning up a Spring context for unit tests, but do like to test our configuration classes (which is called AppConfig in this example, and its settings are prefixed by app):
public class AppConfigTest {
private static AppConfig config;
#BeforeClass
public static void init() {
YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setResources(new ClassPathResource("application.yaml"));
Properties properties = factoryBean.getObject();
ConfigurationPropertySource propertySource = new MapConfigurationPropertySource(properties);
Binder binder = new Binder(propertySource);
config = binder.bind("app", AppConfig.class).get(); // same prefix as #ConfigurationProperties
}
}
The "magic" class you are looking for is PropertiesConfigurationFactory. But I would question your need for it - if you only need to bind once, then Spring should be able to do it for you, and if you have lifecycle issues it would be better to address those (in case they break something else).
This post is going into similar direction but extends the last answer with also validation and property placeholder resolutions.
Spring Boot Binder API support for #Value Annotations
#Value annotations in ConfigurationPropertys don't seem to bind properly though (at least if the referenced values are not part of the ConfigurationProperty's prefix namespace).
import org.springframework.boot.context.properties.bind.Binder
val binder = Binder.get(environment)
binder.bind(prefix, MySettings.class).get

How to set MongoDB ReadPreference in Spring MVC's contextConfigLocation

I am connecting to a MongoDB sharding server via mongodb java driver in Spring MVC. I am using the following versions:
spring-webmvc-3.2.1.RELEASE
mongo-java-driver/2.10.0/mongo-java-driver-2.10.0
spring-data-mongodb-1.2.0.RELEASE
My Mongo options are set in the contextConfigLocation file mvc-dispatcher-servlet.xml
<mongo:mongo host="mongo.sample.com" port="30000">
<mongo:options auto-connect-retry="true"
slave-ok="true"/>
</mongo:mongo>
It works pretty well, but the slave-ok is deprecated by come.MongoDB.ReadPreference. I just wonder if there is any way to set the readPreference for Spring MVC in the contextConfiLocation file.
Declare the following bean
<bean id="readPreferenceSecondary" class="com.mongodb.TaggableReadPreference.SecondaryReadPreference">
</bean>
and
you inject this in your mongotemplate
<bean id="mongoTemplateProdDb" class="org.springframework.data.mongodb.core.MongoTemplate" >
<property name="readPreference" ref="readPreferenceSecondary"></property>
</bean>
Expanding #Trisha's response in to an answer: "Do it in MongoTemplate programmatically" by setting the ReadPreference to SECONDARY.
MongoTemplate template = new MongoTemplate(...);
template.setReadPreference(com.mongodb.ReadPreference.SECONDARY);
In case you are using spring-data-mongodb and have some requirement to use multiple Read Preferences based on find query, you can create multiple Mongo Templates and/or Repositories like
#EnableMongoRepositories(basePackages = {
"com.you.repo.package" }, mongoTemplateRef = "mongoTemplateOne")
#Configuration
public class MongoConfig {
#Bean(name="mongoTemplateOne")
public MongoTemplate getMongoTemplateOne() throws UnknownHostException {
MongoTemplate templateOne = new MongoTemplate(new SimpleMongoDbFactory(new MongoClientURI("YOUR_MONGO_URL")));
templateOne.setReadPreference(ReadPreference.secondaryPreferred());
//setting WriteConcern but not relevant for this thread
templateOne.setWriteConcernResolver(yourWriteConcernResolver());
return templateOne;
}
#Bean(name = "mongoTemplateTwo")
public MongoTemplate getMongoTemplateTwo() throws UnknownHostException {
MongoTemplate templateTwo = new MongoTemplate(new SimpleMongoDbFactory(new MongoClientURI("YOUR_MONGO_URL")));
templateTwo.setReadPreference(ReadPreference.secondaryPreferred());
return templateTwo;
}
private WriteConcernResolver yourWriteConcernResolver() {
return action -> {
if (action.getCollectionName()
.equals("your_collecton")
&& (action.getMongoActionOperation() == MongoActionOperation.SAVE
|| action.getMongoActionOperation() == MongoActionOperation.UPDATE)) {
return WriteConcern.MAJORITY;
}
return action.getDefaultWriteConcern();
};
}
In case you have more than one secondary (replica-set) you can be more specific and tell the mongo driver explicitly which of the secondaries you want to read from, using tags.
On the mongo side you run this command:
db.getMongo().setReadPref('secondaryPreferred',
[{"tagName":"TagVal1"},
{"tagName":"TagVal2"},
{}])
In the code it looks like this:
MongoTemplate template = new MongoTemplate(...)
template.setReadPreference(ReadPreference.secondaryPreferred("your DBObject that reflect your mongo tag names");
Hope it helps.
Here is one more way to do this using Mongo Repositories
#Configuration
#EnableMongoRepositories
class ApplicationConfig extends AbstractMongoClientConfiguration {
#Autowired
private Environment env;
#Value("${spring.data.mongodb.uri}")
public String mongoUri;
#Override
protected String getDatabaseName() {
return env.getProperty("spring.data.mongodb.database");
}
#Override
protected void configureClientSettings(MongoClientSettings.Builder builder) {
builder.applyConnectionString(new ConnectionString(mongoUri)).readPreference(ReadPreference.secondary());
}
}
sample app available #
https://github.com/prashanthmadi/cosmosdb-mongodb-readpreference-springboot-java/blob/main/src/main/java/azure/cosmosdb/mongodb/spring/cosmosdbmongodb/ApplicationConfig.java
If there is a need to mix between primary and secondary for reads depending on the collection, you can set the ReadPreference on the DBCollection object. This helps to avoid complex multiple MongoTemplate configuration. Instead, set collection level preference like below once in the application lifetime. All the reads for that specific collection will go to secondary, while for other collections it goes to primary.
DBCollection dbCollection = mongoTemplate.getCollection(mongoTemplate.getCollectionName(collection));
dbCollection.setReadPreference(ReadPreference.secondaryPreferred());
If you want to know different options to achieve it, please check Spring data mongodb secondary reads
As of spring-mongo-2.0.xsd, slave-ok has been entirely removed, but support has been added for XML config of ReadPreference. Here's the XML from the original question translated for the current XSD:
<mongo:mongo-client host="mongo.sample.com" port="30000">
<mongo:client-options read-preference="SECONDARY_PREFERRED" />
</mongo:mongo-client>

Categories

Resources