Is there a possibility to run some custom SQL query generated via 3rd party tools like e.g. jOOQ and still benefit from Spring Data JDBC mapping features i.e. #Column annotations? I don't want to use #Query annotation.
class Model { #Id private Long id; #Column("column") private String prop; }
class MyRepo {
public Model runQuery() {
val queryString = lib.generateSQL()
// run query as if it has been generated by Spring Data JDBC and map
// results to Model automatically
}
}
Perhaps JdbcTemplate can solve your problem? Specifically, one of the queryForObject() methods may be of interest since your are requesting a single object:
class MyRepo {
private JdbcTemplate jdbcTemplate;
#Autowired
MyRepo(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Model runQuery() {
val query = lib.generateQuery();
return jdbcTemplate.queryForObject(query, Model.class);
}
}
More information and other use cases can be found in the Spring Guide Accessing Relational Data using JDBC with Spring
Related
In my project we are caching the data of a table where we use #NamedQuery for fetching the data (Hibernate queries) and we are also using #NamedNativeQueries and my code is like that
#SpringBootTest
public class NonPolygonIntegeration {
#Autowired
private Repository localRepository;
#MockBean
ServicePropertiesRepository servicePropertiesRepository;
#MockBean
CacheService alendarCacheService;
#Autowired
OlygonService olygonService;
#Test
#Order(1)
#Sql(scripts = {"/sqls/mdm.sql", "/sqls/p.sql","/sqls/q.sql",
"/sqls/x.sql",
"/sqls/y.sql",
"/sqls/z.sql"
}, config = #SqlConfig(encoding = "utf-8"))
void testGetAllActiveCurrencies_forDataPresentInDb() {
}
}
"""
I have data in my local database for all the corresponding tables in use.
query which will get in use are
#Query("Select * FROM Properties WHERE id.Code =:serviceCode AND (endDate IS NULL OR endDate >= CURRENT_DATE)")
and other is
select * from empoyee_table where employee_id = :id
How to handle such scenario?
Please, look at the two code examples bellow which I'm going to use in my Spring Boot project. They both do merely the same thing - add a new object into users table, represented by User entity with username defined as #Id and a unique constraint imposed on email column (there are some other columns as well, but they are not shown here for brevity). Note: I can't simply use save() method from CrudRepository, because it merges existing record with new object if they both have the same username value. Instead, I need to insert a new object with appropriate exception thrown for duplicate data persistence.
My question is about which option should be given a favor. With EntityManager, I don't need to construct SQL statement. Apart from that obvious observation, are there any advantages which one method may offer over the other (especially, in the matter of performance and resources consumption)?
Also, when I read latest books and tutorials about data persistence in Spring Boot, they mainly focus on Spring Data JPA. For example, the 5th edition of "Spring in Action" has no word about Hibernate's EntityMnager. Does it mean that dealing with Hibernate directly can be regarded as kind of "old school" and should generally be avoided in modern projects?
Option #1: Hibernate's EntityManager
#RestController
#RequestMapping(path = "/auth/register", produces = "application/json")
#Transactional
public class RegistrationController {
#PersistenceContext
EntityManager entityManager;
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public Map<String, String> registerNewUser(#RequestBody #Valid User newUser) {
try {
entityManager.persist(newUser);
entityManager.flush();
} catch (PersistenceException ex) {
// parse exception to find out which constraints have been
// broken - either it's duplicate username, email or both
String message = parseExceptionForConstraintNames(ex);
throw new ResponseStatusException(HttpStatus.CONFLICT, messsage);
}
return Collections.singletonMap("message", "Success...");
}
}
Option #2: custom #Query from CrudRepository
#RestController
#RequestMapping(path = "/auth/register", produces = "application/json")
public class RegistrationController {
private final UsersRepository usersRepository;
#Autowired
public RegistrationController(UsersRepository usersRepository) {
this.usersRepository = usersRepository;
}
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public Map<String, String> registerNewUser(#RequestBody #Valid User newUser) {
try {
usersRepository.insert(newUser);
} catch (DataIntegrityViolationException ex) {
// parse exception to find out which constraints have been
// broken - either it's duplicate username, email or both
String message = parseExceptionForConstraintNames(ex);
throw new ResponseStatusException(HttpStatus.CONFLICT, message);
}
return Collections.singletonMap("message", "Success...");
}
}
public interface UsersRepository extends CrudRepository<User, String> {
#Modifying
#Transactional
#Query(nativeQuery = true, value = "INSERT INTO users (username, email) " +
"VALUES (:#{#user.username}, :#{#user.email})")
void insert(#Param("user") User newUser);
}
See this answer for Using JPA repository vs Entity Manager.
Best practice is to not use Repository directly. use Service layer between controller and repository where you can implement the logic for duplicate entries by checking if the record already exist in DB using findByUsername(String username); throw exception if it already exist else save() the object in DB
With the given requirements, the username filed in the entity never qualifies for the #Id.
Why can't u add an explicit id field with some sequence generator for the id filed and just keep the username marked with unique constraint only.
I am new to Graphql and looking into creating a proof of concept to see how it works. I am using Spring Boot (2.2.2.RELEASE) and bringing in the graphql-spring-boot-starter.
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>6.0.1</version>
</dependency>
I have setup my graphql schema as a file on the classpath with the following configuration:
type Order {
orderNumber: ID!
customers: [Customer]
items: [Item]
}
type Customer {
customerNumber: ID!
fullName: String
postalAddresses: [PostalAddress]
}
type PostalAddress {
line1: String
line2: String
city: String
stateCode: String
postalCode: String
postalCodeExtension: String
countryCode: String
}
type Item {
itemId: ID!
fullDescription: String
}
type Query {
findOrdersByCustomerNumber(customerNumber: String): [Order]
}
I have created a root Query class:
#Component
public class Query implements GraphQLQueryResolver {
private OrderService orderService;
#Autowired
public Query(OrderService orderService) {
this.orderService = orderService;
}
public List<Order> findOrdersByCustomerNumber(String customerNumber) {
return this.orderService.findOrdersByCustomerNumber(customerNumber);
}
}
And here is my Order resolver:
#Component
public class OrderResolver implements GraphQLResolver<Order> {
private ItemRepository itemRepository;
private CustomerRepository customerRepository;
#Autowired
public OrderResolver(CustomerRepository customerRepository, ItemRepository itemRepository) {
this.customerRepository = customerRepository;
this.itemRepository = itemRepository;
}
public List<Item> item(Order order) {
return itemRepository.findItemsByOrderNumber(order.getOrderNumber());
}
public List<Customer> customers(Order order) {
return customerRepository.findCustomersByOrderNumber(order.getOrderNumber());
}
}
Everything seems to work fine and I can send a graphql request and get a response. It was actually really easy to implement and impressed with how quickly this library allowed me to do that.
However, here is my issue. It's the dreaded n+1 SQL issue.
So, when I have customer with 162 orders.
1 = Driver SQL (get all the orders for the customer number)
162 = Customer SQL (One query is fired off for each order and it's same customer for each select)
162 = Postal Address SQL (One query is fired off for each customer...note, not included in the code snippets)
162 = Item SQL (assume one item per order).
So, that totals 487 SQL queries. And as a result, this has performance implications. I am using straight JDBC to query the database (no JPA or ORM's at the moment).
My questions is how can I get hold of the GraphQL request in the root Query resolver so that can manipulate the SQL for the dependent objects in the graph? When doing some research, I see that in the Node/Javacript world, there a dataloader utility that can help address this issue (https://github.com/graphql/dataloader)
So, I am unclear on how to solve this with this java implementation. If anyone has any suggestions or sample code, that would be really helpful to see if this POC has any merit.
The problem is that one day we discovered that if we're saving an object in spring boot repository, another objects that are changed in the same method are also updated and persisted in the database.
The curiosity is massive to find out why does this actually happen. I created sample project using Spring Initializr and some template code to show the actual situation (tried to keep the number of dependencies as low as possible).
Using Spring boot version 1.5.11 (SNAPSHOT) and project has following dependencies:
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.mariadb.jdbc:mariadb-java-client:2.1.0')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
Now to the point:
Project has two entities, Pet:
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Pet.class)
public class Pet {
#Id
#GeneratedValue
private long id;
private String type;
public Pet() {}
public String getType() { return type; }
public void setType(String type) { this.type = type; }
}
and User:
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = User.class)
public class User {
#Id
#GeneratedValue
private long id;
private String name;
public User() {}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
Both entities also have repositories, Pet:
#Repository
public interface PetRepository extends CrudRepository<Pet, Long> {
Pet findPetById(Long id);
}
User:
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
User findUserById(Long id);
}
And one simple service where the magic actually happens ( I have pre-saved one Pet and one User object, with different name and type)
#Service
public class UserService {
#Autowired
UserRepository userRepository;
#Autowired
PetRepository petRepository;
public User changeUserAndPet() {
User user = userRepository.findUserById(1L);
Pet pet = petRepository.findPetById(1L);
user.setName("Kevin");
pet.setType("Cow");
userRepository.save(user);
return user;
}
}
Right after calling userRepository.save(user); the Pet object is also updated in the database with new type of 'Cow'. Why exactly does this happen if I only saved the User object? Is this intended to be like this?
There's also one simple controller and simple test endpoint to call the service method which most likely is not important to the question, but I'll still add it here for the sake of completeness.
#RestController
public class UserController {
#Autowired
UserService userService;
#RequestMapping(value = "/test", method = RequestMethod.GET)
public User changeUserAndPet() {
return userService.changeUserAndPet();
}
}
Any explanation / tips are appreciated and feel free to ask extra information / code in github.
The Spring Data repository is a wrapper around the JPA EntityManager. When an entity is loaded, you get the instance, but a copy of the object is stored inside the EntityManager. When your transaction commits, the EntityManager iterates all managed entities, and compares them to the version it returned to your code. If you have made any changes to your version, JPA calculates which updates should be performed in the database to reflect your changes.
Unless you know JPA quite well, it can be tricky to predict when calls are propagated to the database, since flush() is called internally. For instance every time you do a query JPA performs a pre-query flush, because any pending inserts must be send to the database, or the query would not find them.
If you defined a transaction using #Transactional on you method, then pet would be updated even if the user was not saved. When you don't have a transaction, the call to save must trigger the EntityManager to propagate your update to the database. It's a bit of a mystery to me why this happens. I Know that Spring creates the EntityManager inside OpenEntityManagerInViewInterceptor before the Controller is called, but since the transaction is not explicit, it must be created implicitly and there could potentially be multiple transactions.
I always encourage developers to use explicit transactions in Spring, and qualify them with readonly when appropriate.
That's how JPA and the EntityManager works. If you lookup an entity through the repository, it is attached to the EntityManager as managed entity. Any changes that you do to that object, are picked up when a flush is executed by the EntityManager. In fact, you wouldn't even need to call the save method on the repository in your case.
You can find more information about the lifecycle of JPA entities e.g. here: https://dzone.com/articles/jpa-entity-lifecycle
Normally I use annotiations:#Query("SELECT c FROM Country c") with JpaRepositoryor predefined methods like findAll
but in my case I want to generate dynamic query.
String baseQuery =SELECT c FROM Country c`
if(age!=null)
baseQuery+="WHERE c.age=20"
I need to perform same query from code level like this:
Query q1 = em.createQuery("SELECT c FROM Country c");
but I dont use EntityManager in spring boot
How can I generate query from code level?
If you would like to create dynamic queries from code you can take advantage of Spring's JdbcTemplate. Using spring boot it is as simple as injecting JdbcOperations bean to your repository class (assuming you have provided spring-boot-starter-jdbc module to your project).
But remember! This solution uses SQL, not JPQL. That's why you have to use proper tables and columns names in queries and properly map result to objects (i.e. using RowMapper)
This simple example worked fine for me (with different entity, but in same manner - I've adapted it to your example):
#Repository
public class CountryRepository {
#Autowired
private JdbcOperations jdbcOperations;
private static String BASIC_QUERY = "SELECT * FROM COUNTRY";
public List<Country> selectCoutry(Long age){
String query = BASIC_QUERY;
if (age != null){
query += " WHERE AGE = ";
query += age.toString();
}
//let's pretend that Country has constructor Conutry(String name, int age)
return jdbcOperations.query(query, (rs, rowNum) ->
{ return new Country(rs.getString("NAME"), rs.getInt("AGE");}
);
};
}
Then in service or whatever you inject CountryRepository and call method.
Since you're using Spring Boot, you can use Spring Data to create queries in your repository:
#Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
}
Not a 100% on syntax, but should be something similar.
Now you can autowire this class:
#Autowired
public CountryRepository countryRepo;
And all basic methods are already available to you like:
countryRepo.findOne(id);
countryRepo.find();
If you want to make more advanced queries, you can use Spring Data e.g.:
#Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
public Country findByNameAndContinent(String name, String continent);
}
This is just an example (a stupid one) of course and assumes your Country class has the field names 'name' and 'continent' and both are strings. More is available here:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Section 5.3 more specifically.
PS: Make sure your Country class has the #Entity annotation