I am going to design a DAO layer for my application. My focus is that Services just calls DAO which is independent of underlaying implementation.
public interface GenericSearchDao{
List getAll();
List getByQuery(String query);
}
public class UserJdbcSearch implements GenericSearchDao{
public List getAll(){
// Get all users;
}
List getByQuery(String query){
// Get users by query;
}
}
public class UserFileSystemSearch implements GenericSearchDao{
public List getAll(){
// Get all users from file system;
}
List getByQuery(String query){
// Get users by query[this leads to invalid operation];
}
}
public userService {
private GenericSearchDao dao = new UserFileSystemSearch();
public List getUsers(){
rturn dao.getAll();
}
public List getByQuery(String query){
return dao.getByQuery(query);
}
}
Help Required:
What should I do to get rid from 'getByQuery(query)' specific implementaions because datastore can be RDBMS, filesystem, FTP etc.
How should I design my Dao layer generically?
If any one says "remove the getByQuery() from GenericSearchDao" then what should i do in the case where i need data specific to business operation for eg : user with roles , user with products etc..
What should I do to get rid from 'getByQuery(query)' specific implementaions because datastore can be RDBMS, filesystem, FTP etc.
You don't, you just need to provide "generic" query, for instance "name = a" may perform a query in a databae, or look for a file named "a" or pretty much anything else.
What information do you plan to pass in the "query" parameter ? SQL ? I'd replace the String parameter with something business-specific, maybe a small class wiht fields like "name", "surname", etc. The underlying implementation will transform it into SQL, or remote service calls, or other implementation-specific magic.
You may find this approach useful http://www.bejug.org/confluenceBeJUG/display/BeJUG/Generic+DAO+example
For specific impl just throw "UnsupportedOperationException". Good Generic dao implementation can be found here http://code.google.com/p/hibernate-generic-dao/ - I also could adjust the source code to work with hibernate 4 & spring 3
Hey, check this site DAO Implementation. There are some DAO implementations with different Design patterns. I think DAO with Abstact factory would be suitable for you.
Related
Let's say I have a simple REST app with Controller, Service and Data layers. In my Controller layer I do something like this:
#PostMapping("/items")
void save(ItemDTO dto){
Item item = map(dto, Item.class);
service.validate(item);
service.save(item);
}
But then I get errors because my Service layer looks like this:
public void validate(Item item) {
if(item.getCategory().getCode().equals(5)){
throw new IllegalArgumentException("Items with category 5 are not currently permitted");
}
}
I get a NullPointerException at .equals(5), because the Item entity was deserialized from a DTO that only contains category_id, and nothing else (all is null except for the id).
The solutions we have found and have experimented with, are:
Make a special deserializer that takes the ids and automatically fetches the required entities. This, of course, resulted in massive performance problems, similar to those you would get if you marked all your relationships with FetchType.EAGER.
Make the Controller layer fetch all the entities the Service layer will need. The problem is, the Controller needs to know how the underlying service works exactly, and what it will need.
Have the Service layer verify if the object needs fetching before running any validations. The problem is, we couldn't find a reliable way of determining whether an object needs fetching or not. We end up with ugly code like this everywhere:
(sample)
if(item.getCategory().getCode() == null)
item.setCategory(categoryRepo.findById(item.getCategory().getId()));
What other ways would you do it to keep Services easy to work with? It's really counterintuitive for us having to check every time we want to use a related entity.
Please note this question is not about finding any way to solve this problem. It's more about finding better ways to solve it.
From my understanding, it would be very difficult for modelMapper to map an id that is in the DTO to the actual entity.
The problem is that modelMapper or some service would have to do a lookup and inject the entity.
If the category is a finite set, could use an ENUM and use static ENUM mapping?
Could switch the logic to read
if(listOfCategoriesToAvoid.contains(item.getCategory())){ throw new IllegalArgumentException("Items with category 5 are not currently permitted"); }
and you could populate the listOfCategoriesToAvoid small query, maybe even store it in a properties file/table where it could be a CSV?
When you call the service.save(item), wouldn't it still fail to populate the category because that wouldn't be populated? Maybe you can send the category as a CategoryDTO inside the itemDTO that populated the Category entity on the model.map() call.
Not sure if any of these would work for you.
From what I can gather the map(dto, Item.class) method does something like this:
Long categoryId = itemDto.getCategoryId();
Category cat = new Category();
cat.setId(categoryId);
outItem.setCategory(cat);
The simplest solution would be to have it do this inside:
Long categoryId = itemDto.getCategoryId();
Category cat = categoryRepo.getById(categoryId);
outItem.setCategory(cat);
Another option is since you are hardcoding the category code 5 until its finished, you could hard-code the category IDs that have it instead, if those are not something that you expect to be changed by users.
Why aren't you just using the code as primary key for Category? This way you don't have to fetch anything for this kind of check. The underlying problem though is that the object mapper is just not able to cope with the managed nature of JPA objects i.e. it doesn't know that it should actually retrieve objects by PK through e.g. EntityManager#getReference. If it were doing that, then you wouldn't have a problem as the proxy returned by that method would be lazily initialized on the first call to getCode.
I suggest you look at something like Blaze-Persistence Entity Views which has first class support for something like that.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Item.class)
// You can omit the strategy to default to QUERY when using the code as PK of Category
#UpdatableEntityView(strategy = FlushStrategy.ENTITY)
public interface ItemDTO {
#IdMapping
Long getId();
String getName();
void setName(String name);
CategoryDTO getCategory();
void setCategory(CategoryDTO category);
#EntityView(Category.class)
interface CategoryDTO {
#IdMapping
Long getId();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ItemDTO a = entityViewManager.find(entityManager, ItemDTO.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<ItemDTO> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
And in your case of saving data, you can use the Spring WebMvc integration
that would look something like the following:
#PostMapping("/items")
void save(ItemDTO dto){
service.save(dto);
}
class ItemService {
#Autowired
ItemRepository repository;
#Transactional
public void save(ItemDTO dto) {
repository.save(dto);
Item item = repository.getOne(dto);
validate(item);
}
// other code...
}
I am totally new to Hibernate, Spring and postgres database world. There may be difference in "terms" using to describe.
So issue is, i want to fetch a data where it matches given "id" AND "string" using hibernate, JPA annotations in spring boot.
I know basic like repository.findOne(id) but i am not aware of how to get data with two parameters.
I guess i need something like, i am not sure as i am new to db world
SELECT *
FROM student
WHERE type='commerce'
AND id='1'"
I thank you in advance and tutorials to study further is most welcome.
Spring boot provides for you some automatic repositories, so you just create a interface that extends "JpaRepository" and then, create methods with a natural language.
Something like:
#Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
public List<Student> findByIdAndType(Long id, String type);
}
Later on, let's say you want to use this class:
#Autowired
private StudentRepository studentRepository;
public void doSomething() {
List<Student> students = studentRepository.findByIdAndType(1, "commerce");
}
And no, there is no need to provide an implementation to the interface "StudentRepository", as spring data will provide it to you in behind the scenes.
More information on how this works, you can find on proper spring-data documentarion
Cheers, Nikolas
#Repository
public interface UserDao extends User {
public List<User> findByFirstname(String firstname);
}
How could I use above code to retrieve all records?
I tried findByFistname(null);, it doesn't work...
I don't want to use findByFirstname(); because it's possible to have parameter.
Hope you all understand.
Have you considered using a spring data specification? In spring data a specification is a way to wrap the JPA criteria api.
The idea behind the JPA Criteria api is to generate queries programatically, by defining query objects.
Once you have encapsulated the criteria in a specification objects, a single findAll method can be used in a number of scenarios. For example programatically add criteria based input form the user, such as additional search filters etc.
To use this feature a repo will need to extend "JpaSpecificationExecutor"
public interface UserRepository extends JpaRepository<User>, JpaSpecificationExecutor {
List<T> findAll(Specification<T> spec);
}
The find method can then be called with multiple criteria, or the criteria can be built dynamically based on the situation:
List<User> users = userRepository.findAll(where(userLastNameIs("John")).and(userIsArchived()));
Alternatively you can also try query by exampe. The idea here is to provide the actual domain object with the populated search fields to an example matcher. Configure the example matcher to control the search and pass it to the findAll method.
Again the repo will need to implement an interface. Check the documentation for the detailed pros/cons of each approach.
Person person = new Person();
person.setFirstname("Dave");
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths("lastname")
.withIncludeNullValues()
.withStringMatcherEnding();
Example<Person> example = Example.of(person, matcher);
List<Person> people = personRepository.findAll(example);
You should extend your repository from JpaRepository. Be careful with name of repository (It should follow convention). After you inject your UserRepository bean you will have already implemeted by spring data crud methods like findOne(), findAll(), delete() etc.
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
//assume your primary key is Long type
}
Also will be useful documentation
As I got from comments you're trying to achieve ignorance of null values of passed parameters (instead of retrieving all records by findAll()).
Unfortunately, currently, it's not supported by Spring .
You could leverage the #Query annotation and write the query manually in such manner:
#Query("select u from User u where "
+ "(:firstname is null or u.firstname = :firstname)"
+ "(:lastname is null or u.lastname = :lastname)"
)
public List<User> findUserByFirstNameAndLastName(
#Param("firstname") String firstname,
#Param("lastname") String lastname
);
https://spring.io/blog/2011/02/10/getting-started-with-spring-data-jpa/
This is very good tutorial of Spring Data. I suggest you to start with it. tutorial of Spring Data. If you want to go deeper you can read the documentation.
http://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html
I've generic DAO:
#NoRepositoryBean
interface ICrudDao<T, ID extends Serializable> extends Repository<T, ID> {
void delete(T deleted);
List<T> findAll();
T findOne(ID id);
T save(T persisted);
}
To allow services to work on that I have to create interface that allows custom entities get persistence, f.e.:
interface TodoDao extends ICrudDao<Todo, Long> {
}
I've a lot of daos like TodoDao. Then don't deliver any special methods.
Creating a lot of empty interfaces seems a dumb idea. How can create a Generic one?
Edit:
I don't think what you are trying to do is a good idea. At first to register a repository for each Entity seems like boiler plate code, but as the application grows, it will help you to maintain it. Imagine your application to evolve over time like this:
You create a simple entity Person and the Interface PersonRepository. Luckily all basic CRUD operations are included, so far it fits your needs so there is nothing else to do.
As your application grows, Person gets a lot of associated relations, like Address, Job, Hobbies and it would be very inefficient to fetch all associated data everytime you access it, because not always every association is needed. To encounter that, you create your own method in PersonRepository which executes your own NamedQuery to only load certain fields and store it in your DTO needed for the specific view ("SELECT new package.PersonDto(x,y) FROM PERSON WHERE ...).
As time passes by, you find yourself in a situation where you need queries to get executed in dynamic fashion, like pagination or restrictions that only need to be added on certain conditions. So you create a new interface PersonCustomRepository and PersonCustomRepositoryImpl where you write queries in a programatic way:
#PersistenceContext
private EntityManager entityManager;
#Transactional
public List<Person> foo() {
// example for accessing hibernate directly, you could also use QueryDSL and so on
Criteria basicCriteria = entityManager.unwrap().createCriteria(Person.class);
if (someCondition) {
criteria.add(Restrictions.eq("foo", foo));
...
}
...
return criteria.list();
}
Bottom line: Spring data repositories already do a lot of work for you and they are easy to extend, don't try to fight your framework, even it maybe saves you some clicks in the first place.
You can avoid this by making your entities generic.
//you can annotated with #MappedSuperclass
public class BaseBean{
//you can specify the id here
}
public class Todo extends BaseBean {
}
#NoRepositoryBean
interface ICrudDao<T exntends BaseBean, ID extends Serializable> extends Repository<T, ID> {
void delete(T deleted);
List<T> findAll();
T findOne(ID id);
T save(T persisted);
}
I don't think it's possible. See How to create a Generic DAO class using Hibernate Context sessions and Hibernate: CRUD Generic DAO these might help.
I can also think of the Hibernate Session as an example of a single class that deals with the persistence of all types of objects, it just deals with Object type.
It's about passing interface of DTO to DAO.
For example I have following code
public interface User {
String getName();
}
public class SimpleUser implements User {
protected String name;
public SimpleUser(String name) {
this.name = name;
}
#Override
public String getName() {
return name;
}
}
// Mapped by Hibernate
public class PersistentUser extends SimpleUser {
private Long id;
// Constructor
// Getters for id and name
// Setters for id and name
}
I'm using generic DAO. Is it ok if I create DAO with using interface User instead PersistentUser?
User user = new PersistentUser(name);
UserDao.create(user);
I read a lot of topics on stack but not figured out is this approach ok or no. Please help me. Maybe this is stupid and I can achive only problems.
About separating beans.
I did this because some classes I want to share via API module, that can be used outside to create entities and pass them to my application. Because they uses interface I developed so I can pass them to my DAO for persisting.
Generally, I would say it is ok, but there are a few hidden problems. A developer could cast the object down or access some state via a toString method that shouldn't be accessible. If you don't be careful, it could happen that state is serialized as JSON/XML in webservices that shouldn't be serialized. The list goes on.
I created Blaze-Persistence Entity Views for exactly that use case. You essentially define DTOs for JPA entities as interfaces and apply them on a query. It supports mapping nested DTOs, collection etc., essentially everything you'd expect and on top of that, it will improve your query performance as it will generate queries fetching just the data that you actually require for the DTOs.
The entity views for your example could look like this
#EntityView(PersistentUser.class)
interface User {
String getName();
}
Querying could look like this
List<User> dtos = entityViewManager.applySetting(
EntityViewSetting.create(User.class),
criteriaBuilderFactory.create(em, PersistentUser.class)
).getResultList();