This can be for example simple abstract interface for all:
package test.service;
import java.util.List;
public abstract interface JpaService<T> {
void save(T t);
T find(Long id);
void remove(Long id);
List<T> findAll();
long count();
}
And abstract implementation class look like this:
package test.service.impl;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.apache.log4j.Logger;
import test.service.JpaService;
public abstract class JpaServiceImpl<T> implements JpaService<T>{
/** Logger. */
private static final Logger log = Logger.getLogger(JpaServiceImpl.class);
/** Entity manager. */
@PersistenceContext
protected EntityManager em;
/** Reflection util */
protected ReflectionUtil reflectionUtil = ReflectionUtil.get();
/**
* Save entity.
*/
//@Transactional(readOnly = false, propagation=Propagation.REQUIRED, isolation=Isolation.DEFAULT)
public void save(T t) {
log.debug("JpaServiceImpl-Save");
if (reflectionUtil.getIdValue(t) == null) {
// new
log.debug("New");
em.persist(t);
} else {
// update
log.debug("Update");
em.merge(t);
}
}
/**
* Remove entity.
*/
//@Transactional(readOnly = false, propagation=Propagation.REQUIRED)
public void remove(Long id) {
log.debug("remove: " + id);
T t = (T) find(id);
if (t != null) {
log.debug("found try to remove");
em.remove(t);
log.debug("removed");
}
}
/**
* Find entity.
*
*/
@SuppressWarnings( { "unchecked" })
//@Transactional(readOnly = false, propagation=Propagation.REQUIRED)
public T find(Long id) {
log.debug("find!!" + id);
return (T) em.find(reflectionUtil.getModelClass(this), id);
}
/**
* Find all entities.
*/
@SuppressWarnings("unchecked")
//@Transactional(readOnly = true, propagation=Propagation.REQUIRED)
public List<T> findAll() {
log.debug("JpaServiceImpl-findAll");
String queryStr = new StringBuilder().append("select t FROM ").append(
reflectionUtil.getModelClass(this).getName()).append(" t").toString(); //getSimpleName()
log.debug(queryStr);
Query query = em.createQuery(queryStr);
return query.getResultList();
}
public long count() {
String queryStr = new StringBuilder().append("SELECT COUNT(t) FROM ").append(reflectionUtil.getModelClass(this).getName()).append(" t").toString();
Query query = em.createQuery(queryStr);
return (Long) query.getSingleResult();
}
}
Note that even JpaServiceImpl doesnt have any abstract method it is still set to be abstract class. I did this just to enforce that for each entity there is its own implementation of service, since we always going to need few extra special methods for them.
ReflactionUtil its a small class to resolve model beeing used in service and to get id value for it, and looks like this
package test.service.impl;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.Id;
import org.apache.log4j.Logger;
public class ReflectionUtil {
/** Logger */
private static final Logger log = Logger.getLogger(ReflectionUtil.class);
/** Cache Map of scanned obj */
private Map map = new HashMap();
/** single instance */
private static ReflectionUtil instance;
private ReflectionUtil() {}
public static ReflectionUtil get() {
if (instance == null) {
instance = new ReflectionUtil();
}
return instance;
}
public Class getModelClass(Class cls) {
String key = "modelClass-" + cls.getName();
if (!map.containsKey(key)) {
Class<?> modelClass = (Class<?>) (((ParameterizedType) (cls
.getGenericSuperclass())).getActualTypeArguments()[0]);
map.put(key, modelClass);
}
return (Class<?>) map.get(key);
}
public Class<?> getModelClass(Object obj) {
return getModelClass(obj.getClass());
}
public Object getIdValue(Object t) {
try {
Method method = getIdMethod(t);
return (Object) method.invoke(t, null);
} catch (Exception e) {
log.error(e);
return null;
}
}
private Method getIdMethod(Object t) throws SecurityException, NoSuchMethodException {
String key = t.getClass().getName();
//key + "-method"
if (map.get(key + "-method") == null) {
String methodNameForId = getMethodNameForId(t);
log.debug("methodName: " + methodNameForId);
Method m = t.getClass().getMethod(methodNameForId, null);
map.put(key + "-method", m);
}
return (Method) map.get(key + "-method");
}
public String getPropertyNameForId(Object t){
return getMethodOrPropertyNameForId(t, "-propertyNameForId");
}
public String getMethodNameForId(Object t){
return getMethodOrPropertyNameForId(t, "-methodNameForId");
}
private String getMethodOrPropertyNameForId(Object t, String subKey){
String key = t.getClass().getName();
if (map.get(key) == null || map.get(key + subKey) == null ) {
Class<?> modelClass = getModelClass(t);
// set methodNameForId
// finds getter name for field which has id annotation
for (Field f : modelClass.getDeclaredFields()) {
if (f.isAnnotationPresent(Id.class)) {
String propertyNameForId = f.getName();
map.put(key + "-propertyNameForId", propertyNameForId);
String methodNameForId = "get" + capitalize(f.getName());
map.put(key + "-methodNameForId", methodNameForId);
break;
}
}
}
return (String) map.get(key + subKey);
}
/** Capitalize string */
private String capitalize(String str) {
return str.substring(0, 1).toUpperCase()
+ str.substring(1, str.length());
}
}
After that you extend interface like this
package test.service;
import java.util.List;
import test.model.Person;
public interface PersonService extends JpaService<Person> {
List<Person> findByLastName(String name);
}And we can extend implementation service like this
package test.service.impl;
import java.util.List;
import javax.persistence.Query;
import test.model.Person;
import test.service.PersonService;
public class PersonServiceImpl extends JpaServiceImpl<Person>
implements PersonService {
public List<Person> findByLastName(String name) {
Query query = em.createQuery("SELECT p FROM " + reflectionUtil.getModelClass(this).getName() +
" p WHERE p.lastName = '" + name + "' ");
return query.getResultList();
}
}Tell me do you use something similar or?
