I started to develop APIs for iOS app using Java Spring MVC two months ago.
I'll explain my question with an example for clarification purpose.
Let's say I want to update a user's name.
My mySQL user table has columns: id, user_id, email, display_name.
My approach was:
define user:
User.java:
package bean;
public class User {
String displayName;
String email;
String userId;
getters/setters...
}
2.define a DAO:
UserDao.java:
package dao;
import bean.StandardResponse;
public interface UserDao {
public StandardResponse updateUserName(String user_id,String userName);
}
UserDaoImpl.java:
package implement;
import bean.User;
import common.DatabaseConnect;
public UserDaoImpl implements UserDao {
private DatabaseConnect dbConnect;
public UserDaoImpl(DatabaseConnect dbConnect) {
this.dbConnect = dbConnect;
}
public StandardResponse updateUserName(userId,userName) {
if ((userId == null || userId.isEmpty()) ||(userName == null || userName.isEmpty())) return new StandardResponse("Error","Parameters not set!");
String sql = null;
Statement smt = dbConnect.createDataBaseConnectResourse();
StandardResponse result = new StandardResponse("Error","Fail to update the record!");
sql = "update User set user_name="+userName+" where user_id='"+userId+"'";
int updateResult = 0;
try {
updateResult = smt.executeUpdate(sql);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
dbConnect.closeDatabaseConnectResource();
}
if (updateResult == 1) {
result = new StandardResponse("Success","The record has been updated!");
}
return result;
}
}
3.controller
import org.springframework.stereotype.Controller;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import bean.StandardResponse;
import bean.User;
import common.DatabaseConnect;
import common.SpringApplicationContext;
import dao.UserDao;
import implement.UserDAOImpl;
#Controller
#RestController
#RequestMapping("user")
public class UserController {
DatabaseConnect dbConnect = SpringApplicationContext.getApplicationContext().getBean("databaseConnect", DatabaseConnect.class);
UserDao uiObject = new UserDaoImpl(dbConnect);
#RequestMapping(value = "/updateUserName", method = RequestMethod.POST)
public StandardResponse updateUserName(HttpServletRequest request, HttpServletResponse reponses, Model model) {
StandardResponse srObject = uiObject.updateUserName(request.getparameter("userId"),request.getParameter("userName"));
return srObject;
}
}
I just put the crucial classes here. I believe you can understand what I am doing here. So if someone access the URL:****/user/updateUserName providing the userId and userName, he can update the user name of that record. It is functionalbe.
I used the same approach and finished the whole project. It is working. Then, I asked an experienced programmer to look at my code since I figured out all these based on my own understanding. I would like to know how did they do in industry. He gave me some valuable comments.
The whole structure is wrong. I shouldn't have logics in my dao. I should at least have dao, service and action layers. dao only handles database access, service handles all the logic and action handels communication and decide which service to call.
It is very bad approach to hand written SQL in the code. I should use Hibernate.
I should use control inverse and dependency injection.(I am not dealing with this in this post.)
So I started to update my codes to use Hibernate.
define user:
User.java:
package model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="User")
public class User {
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name = "user_id")
private String userId;
#Column(name = "user_name")
private String displayName;
#Column(name = "email")
private String emai;
all getters/setters...
}
dao:
UserDao.java:
package dao;
import model.User;
import java.util.List;
public interface UserDAO {
public void updateUser(User p);
other methods....
}
UserDaoImpl.java
package dao;
import model.User;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Repository;
import model.User;
#Repository
public class PersonDAOImpl implements PersonDAO {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf){
this.sessionFactory = sf;
}
#Override
public void updatePerson(Person p) {
Session session = this.sessionFactory.getCurrentSession();
session.update(p);
}
implementations of other methods....
}
service:
UserService.java:
package service;
import java.util.List;
import model.User;
public interface UserService {
public void updateUser(User p);
other methods....
}
UserServiceImpl.java:
package service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import dao.UserDAO;
import model.User;
#Service
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
#Override
#Transactional
public void updateUser(User p) {
this.userDAO.updateUser(p);
}
implementation of other methods...
}
Now, I just need to write a class to handle the request and call this updateUser service. The whole structure looks better than mine. However, the request won't gave me the whole user object. All I can get is user_id and user_name. I am also not allowed to put logics in dao. So I have to query the table first to get the whole user object and then update the user_name. Comparing to what I used to do, one SQL handles the update. Now using Hibernate, I need 1 query and 1 update.
I found a solution on StackOverflow that I can use HQL to do this in one step. But I though the purpose for using Hibernate is to free us from writing the SQL. If I need to write HQL, why don't I just keep using the writing SQL approach.
So is there a way to do update with Hibernate without query the table first in this structure? Or this is a trade-off for better structure?
I am sorry that I used a really long example for this question. But I couldn't think of other ways to explain the whole story clearly.
So is there a way to do update with Hibernate without query the table first in this structure? Or this is a trade-off for better structure?
Thereis NO tradeoff, you can do updates with HQL(Hibernate Query Language) as well like how you do in SQL.
You can look at the following code:
UserDAOImpl class:
#Repository
//you are calling this in ur code as PersonDAOImpl..change it to UserDAOImpl
public class UserDAOImpl implements UserDAO {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf){
this.sessionFactory = sf;
}
#Override
public int updateUser(String userId, String userName) {
Session session = this.sessionFactory.getCurrentSession();
Query query = session.createQuery("update User set userName =:userName where userId = :userName ");
query.setString("userName", userName);
query.setString(userName, userName);
int result = query.executeUpdate();
return result;
}
}
UserServiceImpl class:
#Service
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
#Override
#Transactional
public void updateUserName(String userId, String userName) {
if(userId !=null && userName != null) {
int result = this.userDAO.updateUser(userId, userName);
if(result==0) //userid not available {
//if userid is NOT available, what to do? check your requirement and handle properly
}
}
} else {
//throw exception
}
}
implementation of other methods...
}
Controller Class:
#Controller
#RestController
#RequestMapping("user")
public class UserController {
#Autowired
private UserService userService;
#RequestMapping(value = "/updateUserName", method = RequestMethod.POST)
public StandardResponse updateUserName(HttpServletRequest request, HttpServletResponse reponses, Model model) {
StandardResponse srObject = userService.updateUserName(request.getparameter("userId"),request.getParameter("userName"));
return srObject;
}
}
Related
So i'm trying to connect java spring to mongoDB and using a findById, but it always gives the null pointer error.
2020-08-04 13:54:01.893 ERROR 8312 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at pt.project.ProvaConceito_BackEnd.mongoDB.UserService.findById(UserService.java:29) ~[classes/:na]
at pt.project.ProvaConceito_BackEnd.mongoDB.mongoDBService.getUserByID(mongoDBService.java:19) ~[classes/:na]
The structure of this project is:
Java
MongoDB
mongoDBService
UserService
Pojos
User
Repositories
UserRepository
I'm gonna share the code I have right now:
mongoDBService
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import pt.project.ProvaConceito_BackEnd.pojos.User;
#RestController
#CrossOrigin(origins="http://localhost:4200")
public class mongoDBService {
UserService userService = new UserService();
#RequestMapping("/concept/user")
public User getUserByID(Integer id) {
return userService.findById(1);
}
}
UserService
package pt.project.ProvaConceito_BackEnd.mongoDB;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pt.project.ProvaConceito_BackEnd.pojos.User;
import pt.project.ProvaConceito_BackEnd.repositories.UserRepository;
import java.util.List;
#Service
public class UserService {
#Autowired(required = false)
private UserRepository userRepository;
public void save(String nome, int idade, String morada) {
userRepository.save(new User(nome, idade, morada));
}
public List<User> findAll() {
return userRepository.findAll();
}
public long count() {
return userRepository.count();
}
public User findById(Integer id) {
return userRepository.findById(id).orElse(null);
}
public void delete(Integer id) {
userRepository.deleteById(id);
}
}
Users (Pojo)
package pt.project.ProvaConceito_BackEnd.pojos;
import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document(collection = "Users")
#Getter
public class User {
#Id
private Integer id;
private String nome;
private int idade;
private String morada;
public User(String nome, int idade, String morada) {
this.nome = nome;
this.idade = idade;
this.morada = morada;
}
}
UserRepository
package pt.project.ProvaConceito_BackEnd.repositories;
import org.springframework.data.mongodb.repository.MongoRepository;
import pt.project.ProvaConceito_BackEnd.pojos.User;
public interface UserRepository extends MongoRepository<User, Integer> {
}
What am I doing wrong here? I think the problem is on mongoDBService...
in this line:
UserService userService = new UserService();
Because I think that it's not being injected, but I don't know how to solve that...
EDIT 1
I have my main class inside pt.project.ProvaConceito_BackEnd:
ProvaConceitoBackEndApplication
package pt.project.ProvaConceito_BackEnd;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class ProvaConceitoBackEndApplication {
public static void main(String[] args) {
SpringApplication.run(ProvaConceitoBackEndApplication.class, args);
}
}
Do not use new to create object that way because spring will not have any knowledge of that object and will not be able to inject it.
Since you have already annotated the UserService with #service you should use #Autowired annotation
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import pt.project.ProvaConceito_BackEnd.pojos.User;
#RestController
#CrossOrigin(origins="http://localhost:4200")
public class mongoDBService {
#Autowired
private UserService userService;
#RequestMapping("/baieuropa/user")
public User getUserByID(Integer id) {
return userService.findById(1);
}
}
Also annotate the UserRepository class with #Repository
package pt.project.ProvaConceito_BackEnd.repositories;
import org.springframework.data.mongodb.repository.MongoRepository;
import pt.project.ProvaConceito_BackEnd.pojos.User;
#Repository
public interface UserRepository extends MongoRepository<User, Integer> {
}
Remove required = false from UserService class
Make sure to have a class with #SpringBootApplication annotation in pt.project.ProvaConceito_BackEnd package.
You need to update your repository and User.class to have an ID type of string instead of Integer. This is required for mongo repositories. May not completely solve your problem but will be a step in the right direction
#Repository
public interface UserRepository extends MongoRepository<User, String> {
}
I would follow the suggestions of other contributors to remove the "new" keyword from your mongoDBService and use the #Repository annotation too.
Autowiring only works if all components that it relies on are autowired too. So in your case because the mongoDBService uses new for the UserService then it expects the Repository to use "new" too. You should instead autowire at all levels and remove the required=false from the autowiring in the UserService.
The repository requires the #Repository annotation otherwise when Spring does its component scan the repository wont be picked up without it. #Repository #RestController etc are all just stereotypes for #Component with varying degrees of additional functionality
I have a bunch of tables for which I need to provide standard CRUD interface. Every time I have to expose a new table, I follow the below pattern.
public interface EntityWithId<TDbEntity> extends Serializable {
public TDbEntity entityId();
}
#Entity
public class DbEntityName implements EntityWithId<Long> {
#Id private Long id;
#Override public Long entityId() {return id;}
// other fields follow
}
public class EntityName {
private Long id;
// other fields follow
// create Entity from DbEntity
public EntityName(DbEntityName dbItem) { ... }
// get DbEntity from Entity
public DbEntityName toDb() { ... }
}
#Repository
public interface DbEntityNameRepository extends CrudRepository<DbEntityName, Long> { }
public interface CrudService<TDbEntity extends EntityWithId<ID>, ID> {
CrudRepository<TDbEntity, ID> getCrudRepository();
// provide default implementation of all CRUD operations here like the below one
default TDbEntity save(TDbEntity entity) { return getCrudRepository().save(entity); }
}
public interface DbEntityNameService extends CrudService<DbEntityName, Long> {
}
#Service
public class DbEntityNameServiceImpl implements DbEntityNameService {
#lombok.Getter #Autowired DbEntityNameRepository crudRepository;
}
#RestController
#RequestMapping("/api/v1/dbservice")
public class EntityNameController {
#Autowired DbEntityNameService dbService;
#PostMapping("/{EntityName}") // this should be replaced by the actual name of the entity
public Long save(#RequestBody EntityName msg) {
return dbService.save(msg.toDb()).entityId();
}
// implement other CRUD end points
}
EntityWithId<T> interface and CrudService<TDbEntity extends EntityWithId<ID>, ID> are defined only once for the system. They provide mechanism to get rid of repeat code in accessing the repo.
As you will notice, the only real code needs to be done to add the fields in the Entity and the DB Entity, and their conversion. Also, I need to roll a new Controller out for each new table.
Question: How can I structure the controller code, in a way that I can inherit the functionality from a base CRUD controller.
Note that, in my real code not all entities are for simple CRUD, and the current structure provides easy way to extend the services
In a nutshell, I am looking for some pattern that will help me provide something like below, where I have a generic Base class, and I can create a subclass with minimal code to expose the controller's end point. Needless to say, the below code will not work as-is to provide the functionality I am looking for.
class BaseController<TEntity, TDbEntity, TId> {
CrudService<TDbEntity, TId> dbService;
#GetMapping("/{TEntity}/{id}")
public TEntity getById(#PathVariable TId id) {
return new TEntity(dbService.getById(id));
}
#PostMapping("/{TEntity}")
public Long save(#RequestBody TEntity msg) {
return dbService.save(msg.toDb()).entityId();
}
}
class EntityNameController : BaseController<EntityName, DbEntityName, Long> {
}
Feel free to provide other suggestions too. My intention is to reduce repeated code in the controller - which is primarily creating the CRUD function, associating it with a CRUD endpoint and invoking the underlying service to do the real work.
EDIT: I understand that I can write a custom annotation processor to generate the standard CRUD functions (almost like how CrudRepository works), but that is not the direction I want to go.
Just to clarify, the intention here is that the standard functionality (like CRUD) can be coded once and for all in a base controller which will expose it, freeing up the child controller to take care of other non-standard work.
I would think for the dbService you could use something like
public interface CrudService<T, ID> {
T findByName(String name);
Set<T> findAll();
T findById(ID id);
T save(T object);
void delete(T object);
void deleteById(ID id);
}
public interface EntityNameService extends CrudService<EntityName, Long> {
}
public class EntityNameServiceImpl implements EntityNameService {
#Inject
private DbEntityNameRepository repository;
// implement all your repo code here
}
And your Base Controller could start like
public class BaseController {
#Autowired
private EntityNameService service;
public String getEntityName(String name) {
service.findByName(name);
}
#PostMapping("/{EntityName}") // this should be replaced by the actual name of the entity
public Long save(#PathVariable String EntityName) {
getEntityName(EntityName);
return dbService.save(msg.toDb()).entityId();
}
}
This was an attempt at getting rid of some boiler plate. The idea was that the business logic would sit in the service and not in the RestController or Repository.
A service could be reused and unit tested well.
QueryDSL with SpringData is your friend:
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
A base repo.
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
#NoRepositoryBean
public interface BaseRepo<T , ID > extends PagingAndSortingRepository<T, ID>, QuerydslPredicateExecutor<T> {}
The real repository that can access very large tables:
import com.querydsl.core.types.Predicate;
import static java.lang.System.out;
import refactor.BaseRepo;
public interface MyEntityRepository extends BaseRepo<MyEntity, String> {
#Override
default long count(Predicate predicate){
//counts on very large tables take forever. Optionally add this
return 0;
}
#Override
default long count(){
//counts on very large tables take forever. Optionally add this
return 0;
}
}
The base service:
import com.querydsl.core.types.Predicate;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
#RequiredArgsConstructor
public class BaseService<T, ID> {
final BaseRepo<T, ID> repo;
public Page<T> findAll(Predicate predicate, Pageable pageable) {
return repo.findAll(predicate, pageable);
}
public Iterable<T> findAllWithoutMeta(Predicate predicate, Pageable pageable) {
return repo.findAll(predicate, pageable);
}
public Iterable<T> findAll() {
return repo.findAll();
}
public T save(T vendor) {
return repo.save(vendor);
}
public T update(T vendor) {
return repo.save(vendor);
}
public void delete(ID id) {
repo.deleteById(id);
}
public boolean exists(ID id) {
return repo.findById(id).isPresent();
}
public Optional<T> getById(ID id) {
return repo.findById(id);
}
}
The real service:
import com.querydsl.core.types.Predicate;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
#Service
public class MyService extends BaseService<MyEntity, String>{
public MyService(MyEntityRepository repo) {
super(repo);
}
#Override
public Page<MyEntity> findAll(Predicate predicate, Pageable pageable) {
return super.findAll(predicate, pageable);
}
}
I decided not to genrify my RestContoller and only write what I code I needed for the CRUD operations I needed. (In some cases delete and put operations are not needed or wanted for example)
This is an implementation of a HATEOAS RESTful API. Investing in a HATEOAS design is not for everyone and every application. This can be substituted by a plain rest controller.
The get here can filter on all the fields in the repository. So you can get http://localhost/api/v1/myapi?name=YourName&age=30
import com.querydsl.core.types.Predicate;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Pageable;
import org.springframework.data.querydsl.binding.QuerydslPredicate;
import org.springframework.hateoas.EntityLinks;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping(path = "/api/v1/myapi", produces = MediaTypes.HAL_JSON_VALUE)
public class MyApiController {
private final MyService service;
private final EntityLinks eLinks;
MyApiController(MyService service, EntityLinks eLinks) {
this.service = service;
this.eLinks = eLinks;
}
#GetMapping
#Transactional(readOnly = true)
ResponseEntity<Resources<Resource<MyEntity>>> findAll(#QuerydslPredicate(root = MyEntity.class) Predicate predicate, Pageable pageable) {
return new ResponseEntity(toResources(service.findAllWithoutMeta(predicate,pageable)), HttpStatus.OK);
}
#GetMapping(value = "/{id}")
ResponseEntity<Resource<MyEntity>> findOne(#PathVariable String id) {
final Optional<MyEntity> findById = service.getById(id);
if (!findById.isPresent()) {
return null;//fixme ResponseEntity.notFound(assembler.);
}
return ResponseEntity.ok(toResource(findById.get()));
}
private Resources<Resource<MyEntity>> toResources(Iterable<MyEntity> customers) {
List<Resource<MyEntity>> customerResources = new ArrayList<>();
for (MyEntity l : customers) {
customerResources.add(toResource(l));
}
return new Resources<>(customerResources);//, selfLink);
}
private Resource<MyEntity> toResource(MyEntity customer) {
Link selfLink = linkTo(methodOn(CallLoggingController.class).findOne(customer.getId())).withSelfRel();
return new Resource<>(customer, selfLink);
}
}
My advice is do not to be obsessed with generic code. Copy and paste is better than super generic code imho.
I am working on a webservice using RESTEasy.
I have an "authentication" webservice with two methods : "login" and "logout".
I have a stateful session scoped bean, UserData with two attributes : "loggedIn", boolean, and "userId", Integer.
I am injecting UserData in my Authentication class. It is working well for the "loggedIn" attribute, when I call "login" method, it is set to true and then it remains true until the session ends.
But strangely it is not working with "userId" attribute. When I call "login" method, I set userId to my user id but right after the "setUserId" method is called, userId is still null.
Here is Authentication class code (without logOut method, unused for now ) :
package com.bini.dev.dilemme.web.api;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import com.bini.dev.dilemme.business.service.UserService;
import com.bini.dev.dilemme.persistence.model.User;
import com.bini.dev.dilemme.web.UserData;
#Path("/auth")
public class AuthenticationApi {
#Inject
private UserData userData;
#Inject
private UserService userService;
#GET
#Path("login")
#Produces("application/json")
public UserData logIn(#QueryParam("mail") String userMail, #QueryParam("password") String userHashedPassword) {
try {
this.logOut();
User user = userService.getUserByMailAddress(userMail.trim());
if (user == null)
throw new Exception("User does not exists");
boolean loggedIn = userService.checkUserPassword(user, userHashedPassword.trim());
if (loggedIn) {
userData.setLoggedIn(true);
userData.setUserId(user.getUserId());
}
return userData;
} catch (Exception e) {
e.printStackTrace();
return userData;
}
}
}
And here is the code of "UserData" class :
package com.bini.dev.dilemme.web;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
#SessionScoped
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC,
getterVisibility = JsonAutoDetect.Visibility.NONE,
setterVisibility = JsonAutoDetect.Visibility.NONE)
public class UserData implements Serializable {
protected Boolean loggedIn;
protected Integer userId;
public UserData() {
this.loggedIn = false;
this.userId = null;
}
public Boolean isLoggedIn() {
return loggedIn;
}
public void setLoggedIn(Boolean loggedIn) {
this.loggedIn = loggedIn;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
}
I tried with or without "Stateful" and "Stateless" annotation. Doesn't change anything.
I really have not idea what to do.
I thought it might be a "setter" syntax error, but I really don't see where.
EDIT : BTW, I am using Weld and RestEASY with WildFly server.
Thanks :)
I get the error.
java.lang.NullPointerException
at org.springframework.orm.hibernate3.support.HibernateDaoSupport.getSession(HibernateDaoSupport.java:143)
at com.walladverts.model.dao.UserDao.findByUsername(UserDao.java:25)
while accessing getSession() in findByUsername
package com.walladverts.model.dao;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository;
import com.walladverts.exceptions.DataNotFoundException;
import com.walladverts.model.entities.User;
import com.walladverts.util.CustomHibernateDaoSupport;#
Repository("userDao")
public class UserDao extends CustomHibernateDaoSupport {
public void save(User user) {
getHibernateTemplate().save(user);
}
public void delete(User user) {
getHibernateTemplate().delete(user);
}
public User findByUsername(String username) throws DataNotFoundException {
Session session = getSession();
Criteria crit = session.createCriteria(User.class);
System.out.println(username);
crit.add(Restrictions.eq("username", username));
crit.setMaxResults(1);
List < User > users = crit.list();
System.out.println(users);
if (users.size() < 1) {
throw new DataNotFoundException();
}
return users.get(0);
}
}
Parent class:
package com.walladverts.util;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public abstract class CustomHibernateDaoSupport extends HibernateDaoSupport {#
Autowired
public void init(SessionFactory factory) {
setSessionFactory(factory);
}
}
Does anybody has an idea why this occurs? It freezes me for developing.
EDIT:
It happens when Spring Secure tries to sign in an user. Also my SessionFactory is working OK when calling this method from Controller.
public abstract class CustomHibernateDaoSupport extends HibernateDaoSupport {
#Autowired
#Qualifier("sessionFactory")
private SessionFactory sessionFactory;
public Session getSession() {
return sessionFactory.getCurrentSession();
}
}
public User findByUsername(String username) throws DataNotFoundException {
Session session = getSession();
// do sth
}
HibernateDaoSupport for a very long time has final getSessionFactory and setSessionFactory methods that can't be overriden.
It was initially supposed to mitigate xml configuration via <property name="sessionFactory" ref="sessionFactory" /> but mixing it #Autowired annotation is also possible.
The you would have to just call getSession() method when needed.
I am in need of a custom constraint which validates that a value is unique. I have been searching the internet for a long time to find a good example. The one I found is using hibernate template which is not recommended anymore so I was wondering if anyone had the expertise to "translate" this code so that it doesn't use the templates. I have tried to do it myself but I don't understand what the getter and setter are for. Here is the code which I got from this helpful guide.
package com.omgo.security.domain.validator;
import java.io.Serializable;
import java.util.List;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;
public class UniqueIDValidator implements ConstraintValidator<Unique, Serializable> {
HibernateTemplate hibernateTemplate;
private Class<?> entityClass;
private String uniqueField;
public void initialize(Unique unique) {
entityClass = unique.entity();
uniqueField = unique.property();
}
public boolean isValid(Serializable property, ConstraintValidatorContext cvContext) {
String query = String.format("from %s where %s = ? ", entityClass.getName(), uniqueField);
List<?> list = hibernateTemplate.find(query, property);
return list != null && list.size() > 0;
}
public SessionFactory getSessionFactory() {
return hibernateTemplate != null ? hibernateTemplate.getSessionFactory() : null;
}
#Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
}
Thank you for your help.
/D
Update
After changing the code I am getting this error:
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
So I'm thinking that maybe I haven't injected the SessionFactory properly?
UniqueIdValidator.java:
package testapp.mis.validator;
import java.io.Serializable;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional; // Added in update 2
public class UniqueIdValidator implements ConstraintValidator<Unique, Serializable> {
#Autowired(required=true)
private SessionFactory sessionFactory;
private Class<?> entityClass;
private String uniqueField;
public void initialize(Unique unique) {
entityClass = unique.entity();
uniqueField = unique.property();
}
#Transactional //Added in update 2 and validation works after that
public boolean isValid(Serializable property, ConstraintValidatorContext cvContext) {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(entityClass);
crit.add(Restrictions.eq(uniqueField, property));
return crit.list().isEmpty();
}
Update 2
I got it working by adding #Transactional in the code.
That uses plain HQL, you can achieve something simpler by using the Criteria API, something like:
public boolean isValid(Serializable property, ConstraintValidatorContext cvContext) {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(entityClass);
crit.add(Restrictions.eq(uniqueField, property));
return crit.list().isEmpty();
}
That should check for uniqueness.
Instead of injecting the hibernate template, just inject the SessionFactory which you also should have configured in your spring context.
Hope you find it useful!