I generate a list of instances which implement interface CheckInterface
using reflection, but some of those instances need to use JPA repository
,as they are new by reflection so Spring will not inject repository for them.
So how can I inject repository into these instance
Or how can I delegate these class to spring as #Bean and iterate them?
#Component
#Log4j
public class ValidationRegister {
public static HashMap<String, CheckItem> itemHashMap = new HashMap<>();
static {
if (itemHashMap.size() == 0) {
Reflections reflections = new Reflections(CheckItem.class.getPackage().getName());
Set<Class<? extends CheckItem>> itemClasses =
reflections.getSubTypesOf(CheckItem.class);
for (Class<? extends CheckItem> checkItemClass : itemClasses) {
try {
itemHashMap.put(checkItemClass.getName(), checkItemClass.newInstance());
} catch (Exception e) {
log.error("ValidationRegister fail : ", e);
}
}
}
}
}
public class LPHYCZ extends CheckItem {
#Autowired
ClaimDataEntityRepository claimDataEntityRepository;
#Override
public boolean check(ClaimRequest request, List<String> errorList) {
ClaimDataEntity claimDataEntity = claimDataEntityRepository.findByClaimId(request.getClaimId());
if (claimDataEntity != null) {
return true;
}
return false;
}
}
When I get instance of LPHYCZ from ValidationRegister.itemHashMap its
claimDataEntityRepository will be null
Just make your Reflections reflections a Spring Bean and get it injected into 'ValidationRegister' then use it to initialize your HashMap your hashmap still can be static, if you want it to be shared by multiple ValidationRegister instances, but the injection won't happen on static fields.
Related
I am getting a DataTypeException when retrieving data since the upgrade to Spring Boot 2.4.x. It worked fine with 2.3.9.RELEASE.
org.jooq.exception.DataTypeException: No Converter found for types MyBaseType and MyInheritType1 at
org.jooq.impl.Tools.converterOrFail(Tools.java:1132) at
org.jooq.impl.Tools.converterOrFail(Tools.java:1148) at
org.jooq.impl.AbstractRecord.get(AbstractRecord.java:270) at
org.jooq.impl.AbstractResultQuery.fetchOne(AbstractResultQuery.java:576) at
org.jooq.impl.SelectImpl.fetchOne(SelectImpl.java:3019)
MyInheritType1 extends MyBaseType.
The classes are using lombok #Data, so they should have the proper setters.
#Data
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "_class")
#JsonSubTypes(
{
#JsonSubTypes.Type(value = MyInheritType1.class, name = "Type1"),
#JsonSubTypes.Type(value = MyInheritType2.class, name = "Type2")
})
public class MyBaseType
{
private UUID id;
private String disclaimerLongText = "";
private LocalDateTime creationTime;
private Map<UUID, String> images = new HashMap<>();
}
The inherited type:
#Data
public class MyInheritType1 extends MyBaseType
{
private String baseMap;
private EnumType someEnum;
private List<LayerType> layerTypes = new ArrayList<>();
private double[] center;
}
I retrieve the data like this:
return dsl.select(PROJECT.DETAILS).from(PROJECT)
.where(PROJECT.ID.eq(id.toString()))
.fetchOne(PROJECT.DETAILS, MyInheritType1.class);
PROJECT.DETAILS is defined as this:
public final TableField<ProjectRecord, ProjectDetails> DETAILS = createField(DSL.name("details"), SQLDataType.JSONB.nullable(false), this, "", new ProjectDetailsBinding());
And ProjectDetailsBinding looks like this:
public class ProjectDetailsBinding extends JsonBBinding<MyBaseType>
{
#Override
protected Class<ProjectDetails> getBindingType()
{
return MyBaseType.class;
}
}
public abstract class JsonBBinding<T> implements Binding<JSONB, T>
{
private ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule());
protected abstract Class<T> getBindingType();
#Override
public Converter<JSONB, T> converter()
{
return new Converter<JSONB, T>()
{
#Override
public T from(JSONB o)
{
if (o == null)
return null;
try
{
return objectMapper.readValue(o.data(), getBindingType());
} catch (Exception e)
{
e.printStackTrace();
}
return null;
}
#Override
public JSONB to(T t)
{
try
{
return JSONB.valueOf(objectMapper.writeValueAsString(t));
} catch (JsonProcessingException e)
{
e.printStackTrace();
}
return null;
}
#Override
public Class<JSONB> fromType()
{
return JSONB.class;
}
#Override
public Class<T> toType()
{
return getBindingType();
}
};
}
[..]
}
Since it worked with 2.3.9.RELEASE, I am wondering what changed in Spring Boot or Jooq, that would cause this different behavior now?
Looks like https://github.com/jOOQ/jOOQ/issues/11762, fixed for 3.15.0 and 3.14.9, to be released soon. You can try building 3.14.9 from github or use a snapshot build from here: https://www.jooq.org/download/versions if you're licensed, to see if that fixes your issue.
Alternatively, you can try to use the fixed version of the DefaultConverterProvider and use that in your Configuration.
Since it worked with 2.3.9.RELEASE, I am wondering what changed in Spring Boot or Jooq, that would cause this different behavior now?
Typically, Spring Boot upgrades come with jOOQ upgrades. You could also downgrade your jOOQ dependency to what you were using before with Spring Boot 2.3.9
I need to Autowire an interface without an implementation, somehow like the
#Repository tag functionality.
#QueryRepository
public interface EddressBookDao {
#ReportQuery
public List<EddressBookDto> loadEddresses(#EqFilter("id") Long id);
}
#Autowired
private EddressBookDao eddressBookDao;
Result result = eddressBookDao.loadEddresses(1L);
I'm thinking of somehow detecting my #QueryRepository Annotation during ClassPathScan and injecting a Proxy of EddressBookDao object on eddressBookDao Autowire.
Right now I am achieving this functionality in a cumbersome way using the following:
#Autowired
public ReportQueryInvocationHandler reportQuery;
private EddressBookDao eddressBookDao;
public EddressBookDao eddressBook(){
if (eddressBookDao == null) eddressBookDao = reportQuery.handle(EddressBookDao.class);
return eddressBookDao;
}
Here is my Handler creating the Proxy:
#Component
public class ReportQueryInvocationHandler implements InvocationHandler {
public <T> T handle(Class<T> clazz){
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws NoSuchFieldException, IllegalAccessException {
Type returnType = method.getReturnType();
Annotation[][] annotations = method.getParameterAnnotations();
Report report = dao.createReport();
for (int i = 0; i < args.length; i++) {
Object argument = args[i];
Annotation[] annotationList = annotations[i];
if (annotationList.length == 0) continue;
for (Annotation annotation : annotationList) {
Class<? extends Annotation> annotationType = annotation.annotationType();
String path = null;
if (annotationType.equals(EqFilter.class)) {
path = ((EqFilter) annotation).value();
report.equalsFilter(path, argument);
break;
}
}
}
return report.list((Class<?>) returnType);
}
And here is how I'm calling my it:
List<EddressBookDto> addressed = dao.eddressBook().loadEddresses(8305L);
All I want is to avoid writing this code
private EddressBookDao eddressBookDao;
public EddressBookDao eddressBook(){
if (eddressBookDao == null) eddressBookDao = reportQuery.handle(EddressBookDao.class);
return eddressBookDao;
}
And write this instead:
#Autowired
private EddressBookDao eddressBookDao;
Spring Data doesn't autowire interfaces although it might look this way. It registers factories which produce proxies implementing the interface.
To do something similar you have to implement the FactoryBean interface.
See the JavaDoc for details. There are also tutorials available.
I have a HsqldbReconciler (for "work" with a HSQLDB database) which I autowired, like:
#Autowired
HsqldbReconciler hsqldbReconciler;
In Future there will be a OracleReconciler, MssqlReconciler, etc. I will need to use them accordingly to the type of connection a user has chosen.
How should I implement this? Usually I would have a kind of factory, which returns only the needed Reconciler. The only way in spring, I can currently imagine, is to Autowire an instance of each Reconciler, then use one of them in the code. Is there a better way?
make a Factory Class that will contain all your beans, e.g
#Component
class Factory{
#Autowired HsqldbReconciler hsqldb;
#Autowired OracleReconciler oracle;
#Autowired MssqlReconciler mssql;
public Object getInstance(String type){
switch(type){
case "mssql" : return mssql;
case "oracle" : return oracle;
// and so on
default : return null;
}
}
}
Now use this Factory as follows
class SomeClass{
#Autowired private Factory factory;
public Object someMethod(){
Object reconciler = factory.getInstance("mssql");
((MssqlReconciler)reconciler).someMethod();
}
}
Define them in your Config with the same name, but different conditions:
#Bean(name = "dbReconciler")
#Conditional(HsqldbReconcilerEnabled.class)
public ReconcilerBase getHsqldbReconciler() {
return new HsqldbReconciler();
}
#Bean(name = "dbReconciler")
#Conditional(OracleReconcilerEnabled.class)
public ReconcilerBase getOracleReconciler() {
return new OracleReconciler();
}
#Bean(name = "dbReconciler")
#Conditional(MssqlReconcilerEnabled.class)
public ReconcilerBase getMssqlReconciler() {
return new MssqlReconciler();
}
create conditions reading from app.properties:
HsqldbReconciler.enabled=true
OracleReconciler.enabled=false
MssqlReconciler.enabled=false
like this:
public class HsqldbReconcilerEnabled implements Condition {
private static final String PROP_ENABLED = "HsqldbReconciler.enabled";
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String property = context.getEnvironment().getProperty(PROP_ENABLED);
return Boolean.parseBoolean(property);
}
}
// etc...
use like:
#Autowired
#Qualifier("dbReconciler")
ReconcilerBase dbReconsiler;
ensure you're not enabling multiple beans at the same time.
The Service class and my repository classes in my spring MVC set up are something like this -
public class ObjectServiceImpl implements ObjectService {
#Autowired
Temp1Repo temp1Repo;
#Autowired
Temp2Repo temp2Repo;
...
}
public interface Temp1Repo extends CrudRepository<Temp1, Integer> {
}
public interface Temp2Repo extends CrudRepository<Temp2, Integer> {
}
Now, in my service class, i am getting a object of a type Temp1, I have to call temp1Repo.save(). If I get an object of Temp2, I have to call temp2Repo.save() and so on...
How do i achieve this?
Seems fairly simple to just have an if statement:
if(object instanceof Temp1) {
temp1Repo.save((Temp1) object);
} else if(object instanceof Temp2) {
temp2Repo.save((Temp2) object);
}
Or perhaps you are looking for a more generic way?
I suppose that you want to regroup all repositories in one. Something like
#SuppressWarnings("rawtypes")
public class ObjectServiceImpl {
#Autowired
private CrudRepository[] repositories;
private Map<Class<?>, CrudRepository> repositoryMap = new HashMap<Class<?>, CrudRepository>();
#PostConstruct
public void init() {
for (CrudRepository r : repositories)
repositoryMap.put(getType(r), r);
}
private Class<?> getType(CrudRepository repository) {
Type[] types = repository.getClass().getGenericInterfaces();
for (Type t : types) {
if (t instanceof ParameterizedType)
return (Class<?>) ((ParameterizedType) t).getActualTypeArguments()[0];
}
throw new IllegalStateException("Check repositories...");
}
public void save(Object entity) {
repositoryMap.get(entity.getClass()).save(entity);
}
public <T> T get(Object id, Class<T> clazz) {
return repositoryMap.get(clazz).findOne(id);
}
....
}
Consider to use EntityManager directly, but could be useful anyway...
Following the code you wrote, Spring will rise an exception at startup time if any injection is missing.
What you want to do is a dynamic Module load, depending on a condition you omitted within your question.
You probably have to use XML configuration style and create a by condition spring context and load the correct one to be used.
Cheers
I am using declarative transactions in Spring. I have a service layer which is annotated with "Transactional". This service layer calls the DAO. I need to enable a hibernate filter in all the dao methods. I don't want to have to explicitly call teh session.enablefilter each time. So is there a way using spring transaction aop etc such that a intercepter can be called when the hibernate session is created?
My Service layer:
#Service("customerViewService")
#Transactional
public class CustomerViewServiceImpl extends UFActiveSession implements CustomerViewService {
private static final Logger log = LoggerFactory.getLogger(CustomerViewServiceImpl.class);
private CustomerDAO daoInstance = null;
private CustomerDAO getCustomerDAO() {
if (daoInstance == null)
daoInstance = DAOFactory.getDao(CustomerDAO.class);
return daoInstance;
}
#Transactional(propagation=Propagation.REQUIRED, rollbackFor=DAOException.class)
public CustomerModel getCustomerModel() throws UFClientException {
CustomerModel model = null;
try {
Customer customerTbl = getCustomerDAO().getCustomerDetail(getUserName());
if (customerTbl == null) {
log.error("DAO-02: No entry found for Customer id- " + getUserName());
throw new UFClientException("DAO-02");
}
model = DozerConverter.hibernateToDto(customerTbl, CustomerModel.class);
}
catch (DAOException e) {
log.error("DAO-01: Not able to fetch entry from database for customer.");
throw new UFClientException();
}
return model;
}
}
My Dao Layer
public class CustomerDAOImpl extends HibernateDaoSupport implements CustomerDAO {
#SuppressWarnings("unchecked")
public Customer getCustomerDetail(String email) throws DAOException {
try {
List<Customer> customers = getHibernateTemplate().find(sb.toString(), email);
if (customers.size() == 0)
return null;
return customers.get(0);
}
catch (Exception e) {
throw new DAOException(e);
}
}
Appreciate your help!!
You can create your own interceptor, and apply it to methods that havbe transactional:
#AroundInvoke("#annotation(transactional)")
public ... handle(Transactional transactional) {
...
}