Spring data mongodb interface configuration and use of _class field - java

I'm migrating from MySQL to mongodb and wondering about couple of things:
1> when using JpaRepository interface and MySQL i need to provide configuration(in my case in the form of settings in application.properties) for it to work otherwise the application fails with a message saying configuration of DataSource failed... but with MongoRepository the application works without any sort of configuration and the data is saved to the default test db. I'd like to know how and where spring makes the initial configuration of the Driver , db name, user and password to use.
2> All objects saved through MongoRepository contain an additional _class field with FQN of the object,
in section 4.6.2 Type mapping the docs say that it needs to do that because
the values held inside that property have to be read in correctly when retrieving the object.
which i didnt fully understand and would love an elaboration on. i created a test with User collection and a few documents in it, some with _class and some without(by inserting the objects using the mongoshell) and the test application had no problem retrieving the User objects that didnt have the _class field and printing them to the console. I also changed the _class field for the collections that had it to "" and it still printed them correctly so how does Spring uses it?

Related

Create users in Oracle, MySQL databases using Springboot - Spring Data JPA

I am very new to Springboot and Spring Data JPA and working on a use case where I am required to create users in different databases.
The application will receive 2 inputs from a queue - username and database name.
Using this I have to provision the given user in the given database.
I am unable to understand the project architecture.
Since the query I need to run will be of the format - create user ABC identified by password;
How should the project look like in terms of model class, repositories etc? Since I do not have an actual table against which the query will be run, do I need a model class since there will be no column mappings happening as such.
TLDR - Help in architecturing Springboot-Spring Data JPA application configured with multiple data sources to run queries of the format : create user identified by password
I have been using this GitHub repo for reference - https://github.com/jahe/spring-boot-multiple-datasources/blob/master/src/main/java/com/foobar
I'll be making some assumptions here:
your database of choice is Oracle, based on provided syntax: create user ABC identified by password
you want to create and list users
your databases are well-known and defined in JNDI
I can't just provide code unfortunately as setting it up would take me some work, but I can give you the gist of it.
Method 1: using JPA
first, create a User entity and a corresponding UserRepository. Bind the entity to the all_users table. The primary key will probably be either the USERNAME or the USER_ID column... but it doesn't really matter as you won't be doing any insert into that table.
to create and a user, add a dedicated method to your own UserRepository specifying the user creation query within a #NativeQuery annotation. It should work out-of-the-box.
to list users you shouldn't need to do anything, as your entity at this point is already bound to the correct table. Just call the appropriate (and already existing) method in your repository.
The above in theory covers the creation and listing of users in a given database using JPA.
If you have a limited number of databases (and therefore a limited number of well-known JNDI datasources) at this point you can proceed as shown in the GitHub example you referenced, by providing different #Configuration classes for each different DataSource, each with the related (identical) repository living in a separate package.
You will of course have to add some logic that will allow you to appropriately select the JpaRepository to use for the operations.
This will lead to some code duplication and works well only if the needs remain very simple over time. That is: it works if all your "microservice" will ever have to do is this create/list (and maybe delete) of users and the number of datasources remains small over time, as each new datasource will require you to add new classes, recompile and redeploy the microservice.
Alternatively, try with the approach proposed here:
https://www.endpoint.com/blog/2016/11/16/connect-multiple-jpa-repositories-using
Personally however I would throw JPA out of the window completely as it's anything but easy to dynamically configure arbitrary DataSource objects and reconfigure the repositories to work each time against a different DataSource and the above solution will force you to constant maintenance over such a simple application.
What I would do would be sticking with NamedParameterJdbcTemplate initialising it by using JndiTemplate. Example:
void createUser(String username, String password, String database) {
DataSource ds = (new JndiTemplate()).lookup(database);
NamedParameterJdbcTemplate npjt = new NamedParameterJdbcTemplate();
Map<String, Object> params = new HashMap<>();
params.put("USERNAME", username);
params.put("PASSWORD", password);
npjt.execute('create user :USERNAME identified by :PASSWORD', params);
}
List<Map<String, Object>> listUsers() {
DataSource ds = (new JndiTemplate()).lookup(database);
NamedParameterJdbcTemplate npjt = new NamedParameterJdbcTemplate();
return npjt.queryForList("select * from all_users", new HashMap<>());
}
Provided that your container has the JNDI datasources already defined, the above code should cover both the creation of a user and the listing of users. No need to define entities or repositories or anything else. You don't even have to define your datasources in a spring #Configuration. The above code (which you will have to test) is really all you need so you could wire it in a #Controller and be done with it.
If you don't use JNDI it's no problem either: you can use HikariCP to define your datasources, providing the additional arguments as parameters.
This solution will work no matter how many different datasources you have and won't need redeployment unless you really have to work on its features. Plus, it doesn't need the developer to know JPA and it doesn't need to spread the configuration all over the place.

Spring Repository without #Query

I am working on a desktop application built using spring framework and one of the part of the application is not working. I found that the repository class does not have any queries with #Query annotation. I haven't encountered it before.
When I try to open the form that uses this, I get an error that the application is not able to connect to the database. The application has 3 databases specified in the application.properties. I have the following questions:
1) How does the following code work without a query specified with #Query annotation. Or where is the query written.
#Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
List<Account> findAccountsByActiveIsTrueAndAccountTypeEquals(String accountType);
List<Account> findAccountsByAccountTypeLike(String type);
}
2) How do we specify which of the database to search for. For example: I have 3 mysql databases currently connected to my application. I wish to access data from DB1 through my Spring boot application through the usual flow of
UI model-> BE Controller/ Service layer -> Repository(Interface) which (usually) has the query written with #Query. How we specify which database this query goes for ?
For your first question I can answer that the JpaRepository has an internal system that analyses the method name you have written and then generates the query that has to be executed to the database.
The #Query annotation is used when the method name and the generated query is not returning the result you wanted to so you specifically tell the compiler which query should be executed.
As mentioned here: https://docs.spring.io/spring-data/jpa/docs/1.5.0.RELEASE/reference/html/jpa.repositories.html
2.3.1 Query lookup strategies.
The JPA module supports defining a query manually as String or have it being derived from the method name.
Declared queries
Although getting a query derived from the method name is quite convenient, one might face the situation in which either the method name parser does not support the keyword one wants to use or the method name would get unnecessarily ugly. So you can either use JPA named queries through a naming convention (see Section 2.3.3, “Using JPA NamedQueries” for more information) or rather annotate your query method with #Query (see Section 2.3.4, “Using #Query” for details).
So basically using a naming convention will do the magic.
Also an interesting question and perfect answer can be found here:
How are Spring Data repositories actually implemented?
For your second question you can refer to this example:
https://www.baeldung.com/spring-data-jpa-multiple-databases
It might be a bit complicated in the beginning but eventually it will work.
He use JPA, JpaRepository has CRUD methodes
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#reference
In your application.properties, you can put your mysql DB info
Why this works without #Query?
Because you are using JpaRepository which provides an easy way to get data based on your entity and it's fields.
Here your Account will have active, accountType etc fields. You can use JPA's query creation keywords such as AND, OR, Equals, Like and many more.
Derived queries with the predicates IsStartingWith, StartingWith, StartsWith, IsEndingWith", EndingWith, EndsWith, IsNotContaining, NotContaining, NotContains, IsContaining, Containing, Contains the respective arguments for these queries will get sanitized. This means if the arguments actually contain characters recognized by LIKE as wildcards these will get escaped so they match only as literals. The escape character used can be configured by setting the escapeCharacter of the #EnableJpaRepositories annotation.
How do we specify which of the database to search?
You can create configuration classes based on your databases and define data sources based on that using #PropertySource.
For more details see example here
#Configuration
#PropertySource({ "classpath:persistence-multiple-db.properties" })
#EnableJpaRepositories(
basePackages = "com.baeldung.multipledb.dao.product",
entityManagerFactoryRef = "productEntityManager",
transactionManagerRef = "productTransactionManager"
)

Spring batch admin. Meta Tables remain empty

I am following spring batch admin.
I want to use database for saving meta data values.
My meta data tables are created, but data does not go to those tables. It still uses in memory for storing metadata.
I know spring uses MapJobRepositoryFactoryBean as the implementation class for jobRepository bean to store data in memory, and we have to change it to
JobRepositoryFactoryBean if we want to store meta data in database.
However, even after changing it, I see no effect. (I have cleaned and recompiled, no issues there)
I have wasted some time searching it, but with no success. Can any one help?
My batch-oracle.properties file is-
batch.jdbc.driver=oracle.jdbc.driver.OracleDriver
batch.jdbc.url=jdbc:oracle:thin:#192.168.2.45:1521:devdb
batch.jdbc.user=hsdndad
batch.jdbc.password=isjdsn
batch.jdbc.testWhileIdle=false
batch.jdbc.validationQuery=
batch.drop.script=classpath:/org/springframework/batch/core/schema-drop-oracle10g.sql
batch.schema.script=classpath:/org/springframework/batch/core/schema-oracle10g.sql
batch.business.schema.script=classpath:oracle/initial-query.sql
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.OracleSequenceMaxValueIncrementer
batch.database.incrementer.parent=sequenceIncrementerParent
batch.lob.handler.class=org.springframework.jdbc.support.lob.OracleLobHandler
batch.grid.size=2
batch.jdbc.pool.size=6
batch.verify.cursor.position=true
batch.isolationlevel=ISOLATION_SERIALIZABLE
batch.table.prefix=BATCH_
After some digging I came to know about the particular convention of properties file(earlier I was giving it name batch-default.properties) . . so now I think it is trying to insert but throwing some SERIALIZABLE exceptions. – Nirbhay Mishra
Try changing the isolationLevelForCreate for the JobRepository to ISOLATION_READ_COMMITTED

Spring Data Mongodb and CrudRepository save(): dropped fields from document if not in POJO model

I'm using Spring Data Mongodb; yesterday I realized that, after calling standard Repository method save(...), it dropped some fields that weren't defined in POJO model: is there any annotation or annotation option to tell spring data to ignore fields that aren't specified in method argument object? Obviously I wanted not to use MongoTemplate save().
If you want to update a mongoDB Document, all you need to use is the save() method and pass the mongoDB auto-created Document _id with the details.

spring jdbc template returns an empty result

I have the following code
resultList = daoResources.jdbcTemplate.query(sql, selectParams, new BeanPropertyRowMapper(resultClass));
SQL when run with the selectParams against database, I get result. The selecting fields name of the sql matches with the fields in the resultClass too. But for above code, I get an empty resultList.
Where could be the problem?
Debugging is your friend in this scenario. I suggest you enable debug logs for jdbc template to see what sql's and bind parameters are sent to database. Below is from the 3.0.x reference doc
All SQL issued by this class is logged at the DEBUG level under the
category corresponding to the fully qualified class name of the
template instance (typically JdbcTemplate, but it may be different if
you are using a custom subclass of the JdbcTemplate class).

Categories