Spring Boot configuration with Morphia? - java

I do not want to utilize the Spring DATA MongoDB support.
I want to leverage the ORM for MongoDB called Morphia.
https://github.com/mongodb/morphia
I want to configure the Morphia with Spring Boot. I want to externalize the configuration of Morphia in a way that it follows the Spring Boot philosophy.
I want to leverage the environment variables for the configuration of Morphia properties.
What would be the Spring Boot approach to achieve this ?
In a simple main program on would do following to get the Morhpia ORM working.
private Morphia morphia;
private MongoClient mongoClient;
morphia = new Morphia();
// Person is an entity object with Morphia annotations
morphia.map(Person.class);
// THESE properties MUST be read from environment variables in Spring BOOT.
final String host = "localhost";
final int port = 27017;
mongoClient = new MongoClient(host, port);
//Set database
// this instance would be autowired all data access classes
Datastore ds = morphia.createDatastore(mongoClient, "dataStoreInstanceId");
// this is how instance would be used in those data accesses classes
Person p = ds.find(Person.class, "username", "john").get();

The Spring Boot like approach would be to create a AutoConfiguration with the needed properties which creates an instance of Datastore as bean.
In the Reference Guide you will find how to set properties and connect to a MongoDB.
An AutoConfiguration for Morphia could look like this:
#Configuration
public class MorphiaAutoConfiguration {
#Autowired
private MongoClient mongoClient; // created from MongoAutoConfiguration
#Bean
public Datastore datastore() {
Morphia morphia = new Morphia();
// map entities, there is maybe a better way to find and map all entities
ClassPathScanningCandidateComponentProvider entityScanner = new ClassPathScanningCandidateComponentProvider(true);
entityScanner.addIncludeFilter(new AnnotationTypeFilter(Entity.class));
for (BeanDefinition candidate : scanner.findCandidateComponents("your.basepackage")) { // from properties?
morphia.map(Class.forName(candidate.getBeanClassName()));
}
return morphia.createDatastore(mongoClient, "dataStoreInstanceId"); // "dataStoreInstanceId" may come from properties?
}
}
You could then autowire your Datastore in other Spring beans the usual way:
#Autowired
private Datastore datastore;
If some points are not correct or unclear just take a look at the existing *AutoConfiguration classes in Spring Boot.

I'm working in a starter for spring boot, here is the repo. Is very easy to use, in some days will update it in maven central.

Related

Javers Mongo Multitenancy Issue

My Spring boot application has a multitenancy architecture and I'm using Javers to audit some data models.
The issue I'm facing is that Javers is not able to resolve the database based on my MongoDatabaseFactory implementation.
So far I've tried creating a Javers Configuration Bean which looks like this:
#Component
public class JaversMongoConfiguration {
#Autowired
CachedMongoClients cachedMongoClients;
#SneakyThrows
public Javers javers() {
MongoRepository javersMongoRepository =
new MongoRepository(cachedMongoClients.getMongoDatabaseForCurrentContext()); // Custom method to fetch the MongoDataBase based on current TenantContext
return JaversBuilder.javers()
.registerJaversRepository(javersMongoRepository)
.build();
}
}
This isn't working as during project build time, Javers instantiates the connection to the default database and dynamically doesn't switch to the tenant database as intended during run time.
Hence, all my audit logs are getting saved at the default database and not in tenant database.
Note: I'm using Javers Spring Boot Mongo starter and Javers Mongo Persistence Maven dependencies.
In Javers there is no integration with Spring multitenancy. Javers is open source, you are encouraged to contribute https://github.com/javers/javers/

How does Spring deal with #Autowired on fields that required a parameter? In this case, JdbcTemplate with DataSource

I have a SpringBoot app that I want to connect to my MySQL database and I want to connect it with JDBC (by itself, not using JPA). And from what I have seen on articles, one way to achieve this is with JdbcTemplate and DataSource objects).
Now I have a RestController where I call my database, "CoffeeShop" which has me with the following class/code:
#RestController
public class MenuController {
#Autowired
private DataSource dataSource;
#Autowired
private JdbcTemplate jdbcTemplate;
private String menuQuery = "SELECT * FROM menu";
#CrossOrigin(origins = "http://localhost:4200")
#GetMapping(path="/menu")
public String getMenu(){
jdbcTemplate.query(menuQuery, (rs, rowNum) -> new Menu(rs.getString("name"))).forEach(
customer-> System.out.println(customer.getName()));
return "worked";
}
private List<Menu> organizeMenu() {
return null;
}
}
If my understanding is correct, I expect that dataSource will be able to see in my application.properties file the following contents when being compiled and then Spring figures out that jdbcTemplate requires it?:
spring.jpa.hibernate.ddl-auto=none
spring.datasource.driverclassname = com.mysql.jdbc.Driver
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/CoffeeShop?useUnicode=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.username=springuser
spring.datasource.password=ThePassword
To my surprise this worked, my code queried the DB and logged the correct output. But I'm not sure how this worked since jdbcTemplate required dataSource?
See the Spring Boot Getting Started | Accessing Relational Data using JDBC with Spring guide, which says:
Spring Boot supports H2 (an in-memory relational database engine) and automatically creates a connection. Because we use spring-jdbc, Spring Boot automatically creates a JdbcTemplate. The #Autowired JdbcTemplate field automatically loads it and makes it available.
This is what you get with the auto-configuration provided by Spring Boot: Fully functional JdbcTemplate automatically created and configured from the application.properties file.
FYI: The JdbcTemplate is already configured to use the DataSource, so you don't need to auto-wire the DataSource. As you can see in your own code, the dataSource field isn't used anywhere, so you should remove it.
It is actually the DataSource that is auto-configured from the application.properties file.

How do I create a session factory in Spring boot with no XML file?

I'm following an online tutorial on how to connect a Java class to a my database using hibernate, however I can't find how to create a SessionFactory since I'm not using a hibernate config file, so
SessionFactory factory = new Configuration().configure()
doesn't work for me. I used spring initializr with Web, JPA, MySQL to create the project, and I've set up my application.properties accordingly (so hibernate IS connected to the database)
I've tried looking at some other answers such as
Spring Boot - Handle to Hibernate SessionFactory
However the answers are a bit outdated/unclear, I'm trying to create an object and begin a transaction with it in my session in my main.java and the only problem I have is getting the FactorySession to work.
In the tutorial it shows a simple solution of
SessionFactory factory = new Configuration().configure("hibernate-cfg.xml.).addAnnotatedClass(Student.class).buildSessionFactory();
Session session = factory.getCurrentSession();
this creates a sessionfactory from the configuration file, and creates a session from that, How can I achieve the same?
You should use spring data jpa , it will greatly simply your job.When spring boot will see data JPA in ur classpath it will try to automatically auto wire entity manager for u.Of course it would need database bean as well which in you case should be coming from MYSQL datasource bean.You have to define ur entity classes with #Entity annotation and follow the standard convention of JPA bean. Then u have to extend CrudRepository in your dedicated repository
Lets say you have Person entity with Long as ID of the bean then ur person repository would look like below
#Repository
public interface PersonRepository
extends CrudRepository < Person , Long> {
}
Then you can inject this repository in ur service classes or if its small project then in controller. Please check out methods in CrudRepository for more information. It has basic methods for save , update ,delete etc for the entity.
Hope that helps.

Spring - JPA - Hibernate how to use EntityManager

I'm trying to build REST application using following tech stack:
Spring
VueJs
JPA (Hibernate)
This is my first experience in writing Sping application and web app development overall.
I have 4 tables in my DataBase:
Language
Sentence
Rule
User
For example in Rule there is :
Rule create(EntityManagerFactory factory, String type, String hint, String help, Language language);
List<Rule> readAll(EntityManagerFactory factory);
Rule readID(EntityManagerFactory factory, int id);
void update(EntityManagerFactory factory, String type, String hint, String help, Language language);
So there is my questions:
When I create Controllers for each table, I use the CRUD methods to modify (or not) my database, and I return a view for my HTML and VueJS part. But my method need an EntityManagerFactory, should I create a field in each Controllers class or this is not how I'm supposed to do ?
Do I need to create a bean file and configure it or persistence.xml and pom.xml are enough?
Thanks
Seems like your first question can be broken up into multiple concerns.
When I create Controllers for each table, I use the CRUD methods to modify (or not) my database, and I return a view for my HTML and VueJS part. But my method need an EntityManagerFactory, should I create a field in each Controllers class or this is not how I'm supposed to do?
Since you have already accepted an answer that recommends the use of spring-data-jpa. You will be dealing with entities and repositories.
Entities are JPA managed beans that will interact with your database.
#Entity
#Table(name = "rule")
public class Rule {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
long id;
String type;
...
#OneToOne
#JoinColumn(...)
Language language;
}
Repositories will provide all the necessary operations required to perform an action against your database. With JPA you can create an interface that extends CrudRepository which would provide you with some CRUD operations that come free with it. findOne(/* id */), delete(), save()
#Repository
public interface RuleRepository extends CrudRepository<Rule, Long> {
// You can easily specify custom finders
public List<Rule> findByType(String type);
}
But my method need an EntityManagerFactory, should I create a field in each Controllers class or this is not how I'm supposed to do?
It's typically frowned upon to have a request/response object to be JPA entity. See the linked answer for should i use jpa entity in rest request and/or response
There are multiple approaches that you can take to take a controller request and send a response to your client side project.
#Controller
public class RuleController {
#Autowired
private RuleRepository ruleRepository;
// Approach 1: Use Request Parameters - enforce inputs
#PostMapping("/rule/:id")
public Rule postWithRequestParams(#PathParam("id") Long id,
#RequestParam("type") String type,
#RequestParam("hint") String hint,
#RequestParam("languageField1") String languageField1) {
Rule inputRule = new Rule(id, type, hint, new Language(languageField1));
Rule responseRule = ruleRepository.save(inputRule);
return responseRule; // I would imagine you would want to set up a model for the response itself
}
// Approach 2: Use RequestBody - serialize Rule from the request
#PostMapping("/rule/:id")
public Rule postWithRequestParams(#PathParam("id") Long id, #RequestBody Rule inputRule) {
Rule responseRule = ruleRepository.save(inputRule);
return responseRule;
}
Do I need to create a bean file and configure it or persistence.xml and pom.xml are enough?
If you have added spring-boot-starter-data-jpa as a dependency, a lot of the bean configuration has already been done for you.
In your main/src/resources (you should have an application.properties or application.yml)
spring.datasource.url= # JDBC url of the database.
spring.datasource.username= # Login user of the database.
spring.datasource.password= # Login password of the database.
Spring does a lot of the magic and heavy lifting for you.
If you are using spring Boot then you don't need entity manager. All you need to do is to define Datasource in you properties file. And create a Bean in our Configuration class just like:
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource datasource() {
return DataSourceBuilder.create().build();
}
Now rest of the things you can handle with repositories.They will be like:
import org.springframework.data.repository.CrudRepository;
public interface RuleRepository extends CrudRepository<Rule, Long> {
}
In your controllers you will use it like:
#Autowired
private RuleRepository ruleRepository;
#Get
#Path("/getRule/:id")
public Rule find(#PathParam("id")Long id){
return ruleRepository.findOne(id);
}
These dependencies I used in my gradle project. You will find Maven version for same:
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile group: 'mysql', name: 'mysql-connector-java'
You definitely need to have a look on Spring Boot(http://start.spring.io), it allows easier to start web app development. As for persistence layer you could use Spring Data JPA(already includes Hibernate) module which also can be easily integrated with Spring Boot. The beauty of Spring Data that is already have written by default most queries like save(), remove(), find() and so on. You only need to define Objects which will be used by Spring Data.
Update: See my Spring Boot REST API example here

How can you load initial data in MongoDB through Spring Boot?

Is it possible to load initial data in a MongoDB database using src/main/resources/data.sql or by any other file?
I understand that data.sql is used for SQL DB's whereas MongoDB is a NOSQL DB. But just wanted to know if there is any equivalent of data.sql for NOSQL DB's.
While googling I found out this SO link (Spring Boot - Loading Initial Data) which does what I am looking for but still it's not a standalone file data.sql.
To load initial data you can use db migration tool like MongoBee
It's very useful option to handle data initialization in java. You just need to configure #Bean public Mongobee mongobee in your spring boot and setup component scan for data ChangeLogs where data creation actually happens.
You can use a repository populator with Spring Data MongoDB. Let me demonstrate this with a code sample in Kotlin:
#Configuration
class TestApplicationConfig {
#Value("classpath:test_data.json")
private lateinit var testData: Resource
#Bean
#Autowired
fun repositoryPopulator(objectMapper: ObjectMapper): Jackson2RepositoryPopulatorFactoryBean {
val factory = Jackson2RepositoryPopulatorFactoryBean()
// inject your Jackson Object Mapper if you need to customize it:
factory.setMapper(objectMapper)
factory.setResources(arrayOf(testData))
return factory
}
}
Put test_data.json in resources directory.
you can define your data in json/xml and use populator elements of the repository to load the data.
https://docs.spring.io/spring-data/mongodb/docs/2.0.9.RELEASE/reference/html/#core.repository-populators

Categories