Spring does not inject SessionFactory when calling CustomValidator second Time - spring

i am developing a application using spring and hibernate. i have created a custom validator for for checking duplicate value in the database for that i need to inject hibernate SessionFactory in the CustomValidator. it works fine for on a single field but when i apply it on two or more field i get a NullPointerException it seems like spring does not inject the SessionFactory while calling validator second time.
here is code of entity class
public class CenterRegistration implements Serializable {
#Column(unique=true)
#UniqueKey(message="Email Id already exist please choose another Email Id ", table = "CenterRegistration", column = "email")
private String email;
#UniqueKey(message="UserName already exist please choose another username", table = "CenterRegistration", column = "userName")
#Column(unique = true)
private String userName;
And here is code of customvalidator implementing class
public class UserNameConstraintValidator implements
ConstraintValidator<UniqueKey, String> {
#Inject
private SessionFactory sessionFactory;
public boolean isValid(String value, ConstraintValidatorContext context) {
System.out.println("in is valid value=" + value + " " + sessionFactory);
if (sessionFactory == null) {
return true;
}
Session session = sessionFactory.openSession();
it works fine when i apply this validator on a single field on my entity but when applying on second spring injects null into the sessionfactory.
if (sessionFactory == null) {
return true;
}
i have used these just to avoid NullPointerException.

Related

Hibernate custom sequence generator - Table doesn't exist

I'm using a custom class to generate id:
public class StringPrefixedSequenceIdGenerator extends SequenceStyleGenerator
Also using this class on entity attribute:
#Id
#GenericGenerator(name = "pipeline_seq_generator", strategy = "com.model.StringPrefixedSequenceIdGenerator", parameters = {
#Parameter(name = StringPrefixedSequenceIdGenerator.INCREMENT_PARAM, value = "50") })
#GeneratedValue(generator = "pipeline_seq_generator", strategy = GenerationType.SEQUENCE)
#Column(name = "pipeline_id", unique = true, nullable = false, length = 100)
public String getPipelineId() {
return this.pipelineId;
}
The problem is that hibernate still try to get the sequence from database:
select next_val as id_val from pipeline_seq_generator for update
Error: java.sql.SQLSyntaxErrorException: Table 'table.pipeline_seq_generator' doesn't exist
EDIT: The #Override generate method is causing hibernate to call sequence from database when I pass super.generate(session,object) as a parameter in my method :
#Override
public Serializable generate(SharedSessionContractImplementor session,
Object object) throws HibernateException {
return String.format(format, LocalDate.now(), super.generate(session, object));
}
I changed the implementation of generate method to not use the super.generate(session, object):
#Override
public Serializable generate(SharedSessionContractImplementor session,
Object object) throws HibernateException {
LocalTime localTime = LocalTime.now();
return prefix + String.format(format, LocalDate.now()) + localTime.getHour() + localTime.getMinute() + localTime.getSecond();
}
In this model I'm only using my implementation without use sequence database.
In
public void configure(Type type, Properties properties,
ServiceRegistry serviceRegistry) throws MappingException {
// ignore the (String) Type input parameter and use a new LongType()
super.configure(new LongType(), properties, serviceRegistry);
String Types can not be generated by a sequence in hibernate (liquibase)
after this you can just use:
String toString = super.generate(session, object).toString();
in the generate method and add a prefix .

Overriding user injected by #CreatedBy Annotation in AuditorAware Implementation

Following is my implementation of AuditAware.
#Component
public class AuditorAwareImpl implements AuditorAware<String> {
#Log
private Logger logger;
#Override
public String getCurrentAuditor() {
String user = "SYSTEM";
try {
final Subject subject = SecurityUtils.getSubject();
if (subject != null) {
final Session session = subject.getSession(false);
if (session != null) {
final User userObj = (User) session.getAttribute("user");
if (userObj != null) {
user = userObj.getUsername();
}
}
}
} catch (final Exception e) {
this.logger.error("getCurrentAuditor", "No logged in user information found.", e);
}
return user;
}
}
As we know, The value of user returned by AuditorAwareImpl injected by Spring in attribute marked by #CreatedBy annotation as below.
#CreatedBy
#Column(name = "CREATED_BY", length = 150, nullable = false, updatable = false)
private String createdBy;
There are two problems I want to solve.
I am triggering a separate thread which saves
thousands of objects in database. If session times out in between, session object becomes null and AuditAwareImpl throws error and hence background process errors out.
I also want to run few quartz Schedular related jobs in which in which session object is itself not available. But I still want to inject some hard coded user in that case. But how do I do it?
One thing that I have tried at individual object level, using following code is,
#Transient
private String overRiddenUser;
#PrePersist
public void updateCreatedBy(){
if(overRiddenUser!=null){
this.createdBy=overRiddenUser;
}
}
I explicitly set overRiddenUser="Admin" whenever I want to override user injected by spring in createdBy and overwrite it using PrePersist method. But even this does not seem to work since before even I overwrite this value, spring tries to populate created by using value returned by AuditAware. If session is already expired by then the process errors outs! How do I solve this?
I found the solution to above problems as below.
Step 1
I defined a bean as below that contains a HashMap. This map will store current thread name as key and mapped username as value.
public class OverRiddenUser {
Map<String,String> userThreadMap= new HashMap<>();
//add getters/setters
}
Step 2
Initialised this bean on startup in class annoted by #Configuration.
#Bean("overRiddenUser")
public OverRiddenUser overRideUser(){
OverRiddenUser obj = new OverRiddenUser();
return obj;
// Singleton implementation can be done her if required
}
Step 3
Auto wired this bean in the impl class where I want to override session user.
#Autowired
OverRiddenUser overRiddenUser;
Assigned a random name to currently running thread.
RandomString randomThreadName = new RandomString(9);
Thread.currentThread().setName(randomThreadName.toString());
And then put required user name for current thread in hashmap as below.
try {
if(overRiddenUser!=null){
overRiddenUser.getUserThreadMap().put(Thread.currentThread().getName(),"My Over ridden username");
}
// Entire Database related code here
} catch (Exception e){
// handle the exceptions thrown by code
} finally {
//Perform clean up here
if(overRiddenUser!=null){
overRiddenUser.getUserThreadMap().remove(Thread.currentThread().getName());
}
}
}
Step 4
Then I modified AuditAwareImpl as below.
#Component
public class AuditorAwareImpl implements AuditorAware<String> {
#Log
private Logger logger;
#Autowired
OverRiddenUser overRiddenUser;
#Override
public String getCurrentAuditor() {
String user = "SYSTEM";
try {
if(overRiddenUser!=null && overRiddenUser.getUserThreadMap()!=null && overRiddenUser.getUserThreadMap().get(Thread.currentThread().getName())!=null){
user=overRiddenUser.getUserThreadMap().get(Thread.currentThread().getName());
}else{
final Subject subject = SecurityUtils.getSubject();
if (subject != null) {
final Session session = subject.getSession(false);
if (session != null) {
final User userObj = (User) session.getAttribute("user");
if (userObj != null) {
user = userObj.getUsername();
}
}
}
}
} catch (final Exception e) {
this.logger.error("getCurrentAuditor", "No logged in user information found.", e);
}
return user;
}
}
So if the currently running thread accesses AuditAwareImpl then it gets custom user name which was set for that thread. If this is null then it pulls username from session as it was doing earlier. Hope that helps.

Hibernate -validator group sequence provider getDefaultSequenceProvider gets null as input

I am using the hibernate validator group sequence and want to execute the groups in a sequence based on business rules. But the input to the groupSequenceProvider for its getValidationGroups is always null, and hence custom sequence never gets added.
My request object:
#GroupSequenceProvider(BeanSequenceProvider.class)
public class MyBean {
#NotEmpty
private String name;
#NotNull
private MyType type;
#NotEmpty(groups = Special.class)
private String lastName;
// Getters and setters
}
Enum type:
public enum MyType {
FIRST, SECOND
}
My custom sequence provider:
public class BeanSequenceProvider implements DefaultGroupSequenceProvider<MyBean> {
#Override
public List<Class<?>> getValidationGroups(MyBean object) {
final List<Class<?>> classes = new ArrayList<>();
classes.add(MyBean.class);
if (object != null && object.getType() == MyType.SECOND) {
classes.add(Special.class);
}
return classes;
}
}
Group annotation:
public interface Special {
}
When I execute the above code, I get the input MyBean object as null and cannot add the custom sequence. What am I missing? I am using hibernate-validator version as 5.4.1.Final

LazyInitializationException in AOP logger aspect

I have an app build with Spring, JSF JPA and Hibernate.
I have an aspect that "watches" every update*, create*,delete* method from my service layer of the application. This aspect logs the method params, apply toString to every param and them log them in the database. The problem is that I use domain objects in jsf and when i try to update* something i get a LazyInitializationException when toString() is applied to the method param.
One solution would be to remove from toString all params that represents other objects but then the operation has no sense as i do not log the details that interests me.
ie. I have an entity called Price which has a dependency PriceList:
public class Price extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "price")
private Double price;
//bi-directional many-to-one association to TelCoPriceList
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "price_list_id")
private PriceList priceList;
.....................
}
And the price is used into AddPriceForm.xhtml, which is the jsf form. The JSF AddPriceMB has a reference to PriceService which performs the actual update.
The PriceService is "monitored" by this aspect:
#Aspect
#Named("auditLogAspectImpl")
public class AuditLogAspectImpl implements AuditLogAspect {
#Inject
private UserService userService;
#Inject
private AuditLogService auditLogService;
#Override
#AfterReturning(pointcut = "execution(* com.videanuadrian.core.impl.services..*.save*(..)) or execution(* com.videanuadrian.core.impl.services..*.update*(..)) or execution(* com.videanuadrian.core.impl.services..*.delete*(..))", returning = "retVal")
public boolean afterLogEvent(JoinPoint joinPoint,Object retVal) {
String className = joinPoint.getTarget().getClass().getName();
String methondName = joinPoint.getSignature().getName();
...................................................................
StringBuffer logMessage = new StringBuffer();
Object[] args = joinPoint.getArgs();
//for login action we only get the username and hash the password
if (methondName.compareTo("login") == 0){
logMessage.append(args[0]);
}else {
int argsLen = args.length;
//else log all the parameters
for (int i = 0; i < argsLen; i++) {
// this is where the exception occurs !!!!!!!!!!!!!!!!!!!!!!!!!!!
logMessage.append(args[i]).append(",");
}
if (argsLen > 0) {
logMessage.deleteCharAt(logMessage.length() - 1);
}
}
//some save/update methods return Boolean
Boolean status = false;
if (retVal instanceof Boolean){
status = (Boolean) retVal;
}
//some return the ID of the object inserted,and if these methods return an integer the status is true, if they return null, the status si false
if (retVal instanceof Integer){
if (retVal!=null)
status = true;
}
auditLogService.addAuditLogEvent(uid, className+"."+methondName, status,logMessage.toString());
return true;
}
On a prior version I did not had this problem because I had used DTO objects and the conversion between DTO and Domain Objects was performed in the service layer. The error is very clear, as my session was closed long ago compared to the time that I perform this toString() operation.
Any idea how can i achieve this without using openSessionInView or extended persistence context? Or maybe my approach is not good....

How to write a search class to accept any type of parameter?

I'm using spring mvc and I created the CRUD functionality. But I want to create a search function that will allow me to find a user by any parameter (variable) as 'userid' or 'username' or 'lastname' or 'social security number' or whatever.
My userid is an integer type.
How can I do that? What is the SQL query for that?
How can I check if the input is integer or string and then go through the database by the given parameter and search for the user?
If you are using Hibernate for data access you can easily create universal finder using criteria API:
Abstract DAO class:
public abstract class AbstractHibernateDAO<T> {
private static final String PARAM_VALUE_PARAMETER = "paramValue";
private final Class<T> clazz;
#Autowired
private SessionFactory sessionFactory;
public AbstractHibernateDAO(Class<T> clazz) {
this.clazz = clazz;
}
public T findOne(String paramName, Object paramValue) {
Session session = sessionFactory.getCurrentSession();
#SuppressWarnings("unchecked")
T fetchedObject = (T) session.createCriteria(clazz).add(Restrictions.eq(paramName, paramValue)).uniqueResult();
return fetchedObject;
}
// Other CRUD methods.
}
Concrete DAO class for entity:
#Repository
#Transactional
public class ProductHibernateDAO extends AbstractHibernateDAO<Product> {
public ProductHibernateDAO() {
super(Product.class);
}
}
Or if you prefer to use HQL instead of Criteria API you can rewrite search method as:
public T findOne(String paramName, Object paramValue) {
Session session = sessionFactory.getCurrentSession();
StringBuilder queryText = new StringBuilder();
queryText.append("from ");
queryText.append(clazz.getSimpleName());
queryText.append(" where ");
queryText.append(paramName);
queryText.append("=:");
queryText.append(PARAM_VALUE_PARAMETER);
#SuppressWarnings("unchecked")
T fetchedObject = (T) session.createQuery(queryText.toString()).setParameter(PARAM_VALUE_PARAMETER, paramValue).uniqueResult();
return fetchedObject;
}
In this article you can find very good description how to create generic DAO with hibernate (Or if you prefer JPA there are also described how to do this with JPA).
Or if you prefer to use JDBC for data access I recommend you to look at Spring's JdbcTemplate. It simplifies development a lot. Here how you can implement universal finder using JdbcTemplate:
#Repository
#Transactional
public class ProductJDBCDAO implements DAO<Product> {
private static final String TABLE_NAME = "product";
#Autowired
private JdbcTemplate jdbcTemplate;
public Product findOne(String paramName, Object paramValue) {
RowMapper<Product> rowMapper = new RowMapper<Product>(){
public Product mapRow(ResultSet rs, int rowNum) throws SQLException {
long productId = rs.getLong("product_id");
// Other properties
Product product = new Product(...);
return product;
}
};
StringBuilder queryText = new StringBuilder();
queryText.append("select * from ");
queryText.append(TABLE_NAME);
queryText.append(" where ");
queryText.append(paramName);
queryText.append("=?");
Product fetchedObject = jdbcTemplate.queryForObject(queryText.toString(), rowMapper, paramValue);
return fetchedObject;
}
// Other CRUD methods
}
Ass you can see in all examples you don't need explicitly specify parameter type, you just add it as Object parameter.
If you will work with direct JDBC in such case I recommend you to use PreparedStatement and it's setObject(..) method. Query text will be similar to shown in the example with JdbcTemplate.

Resources