Find node by its property name using neo4j OGM - java

I have my SkillCluster class as follows
public class SkillCluster {
#Id #GeneratedValue
private Long id;
private String Name;
private String CleanedText;
#Relationship(type = "BelongsTo", direction = Relationship.INCOMING)
private Set<Skill> contains = new HashSet<>();
}
Its corresponding DAO class
import org.neo4j.ogm.session.Session;
import com.models.GenericDAO;
public class SkillClusterDAO extends GenericDAO<SkillCluster>{
public SkillClusterDAO(Session session) {
super(session);
}
protected Class<SkillCluster> getEntityType() {
return SkillCluster.class;
}
}
and my GenericDAO class as
public abstract class GenericDAO<T> {
private static final int DEPTH_LIST = 0;
private static final int DEPTH_ENTITY = 1;
private Session session;
public long filterCount(Iterable<Filter> filters){
return session.count(getEntityType(), filters);
}
public T find(Long id) {
return session.load(getEntityType(), id, DEPTH_ENTITY);
}
public T find(String name) {
return session.load(getEntityType(), name, DEPTH_ENTITY);
}
public void delete(Long id) {
session.delete(session.load(getEntityType(), id));
}
public void createOrUpdate(T entity) {
session.save(entity, DEPTH_ENTITY);
//return find(entity.id);
}
protected abstract Class<T> getEntityType();
public GenericDAO(Session session) {
this.session = session;
}
}
I wanted to get the cluster node by matching the Node property Name by doing this
skillSessionFactory = new SessionFactory(skillConfiguration, "com.skill.models");
skillSession = skillSessionFactory.openSession();
skillClusterDAO = new SkillClusterDAO(skillSession);
SkillCluster clusterNode = skillClusterDAO.find(cluster_name);
I am getting the follwing error -
java.lang.IllegalArgumentException: Supplied id must be of type Long (native graph id) when supplied class does not have primary id - com.models.SkillCluster

You have this error because the name property isn't a Long.
Even if your name property would have been a Long too, it wouldn't works because it would have been fetched a wrong node.
session.load(...) works for internal node Id, or with property marked as #Id or primary index #Index(primary = true).
If you need to find node by its property otherwise than the primary key, you could use session.loadAll(...) with filter.
public abstract class GenericDAO<T> {
...
import java.util.Collection;
import java.util.Optional;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.ogm.cypher.Filter;
...
public T find(Long id) {
return session.load(getEntityType(), id, DEPTH_ENTITY);
}
public T find(String name) {
final String propertyName = "name";
Filter filter = new Filter(propertyName, name);
Collection<T> results = session.loadAll(getEntityType(), filter, DEPTH_ENTITY);
if( results.size() > 1)
throw new CustomRuntimesException("Too results found");
Optional<T> entity = results.stream().findFirst();
return entity.isPresent() ? entity.get() : null;
}
...
}

Related

Springboot posgresql reposity bean can't be autowired

I have simple Sprinngboot app where actual database is PostgreSQL.
My model:-
#Table("carrier")
#Entity
public class MyCarrier {
#Id
#Column("id")
private UUID id;
#Size(
max = 100
)
#Column("carrier_name")
private String carrierName;
#Size(
max = 3
)
#Column("smdg_code")
private String smdgCode;
#Size(
max = 4
)
#Column("nmfta_code")
private String nmftaCode;
public MyCarrier() {
}
public UUID getId() {
return this.id;
}
public String getCarrierName() {
return this.carrierName;
}
public String getSmdgCode() {
return this.smdgCode;
}
public String getNmftaCode() {
return this.nmftaCode;
}
public void setId(final UUID id) {
this.id = id;
}
public void setCarrierName(final String carrierName) {
this.carrierName = carrierName;
}
public void setSmdgCode(final String smdgCode) {
this.smdgCode = smdgCode;
}
public void setNmftaCode(final String nmftaCode) {
this.nmftaCode = nmftaCode;
}
protected boolean canEqual(final Object other) {
return other instanceof Carrier;
}
}
Repository:-
#Repository
public interface MyCarrierRepository extends JpaRepository<MyCarrier, Long> {
}
Controller:-
#RestController
#RequestMapping(path = "/upload")
public class ReactiveUploadResource {
Logger LOGGER = LoggerFactory.getLogger(ReactiveUploadResource.class);
private final SqlRequestHandler sqlRequestHandler;
#Autowired
private MyCarrierRepository myCarrierRepository;
public ReactiveUploadResource(SqlRequestHandler sqlRequestHandler) {
this.sqlRequestHandler = sqlRequestHandler;
}
}
I got this error:-
Description:
Field myCarrierRepository in com.consumer.controller.ReactiveUploadResource required a bean of type 'com.consumer.repository.MyCarrierRepository' that could not be found.
What is missing? Why Springboot doesn't find this repository?
You have to put the repository inside the package at the same level as Application class the packages to allow Spring boot to scan it

get the return value of the method which is from Generic class in java

I created a parent class Repo which has methods for insert, delete, display and delete objects in a list. Repo is a generic class. I created a child classes for Repo (like DepartmentRepo class)and pass Department, Employee, etc.. classes. I want perform insert, delete, display and delete operations on any class objects that passed to Repo class.
I need to get the return value of the method "get" which is from Generic class in java. I can only get the method name from Generic here I mention the code files
public class Department {
private long Id;
private String Name;
private String Location;
public Department() {
}
public Department(long id, String name, String location) {
super();
Id = id;
Name = name;
Location = location;
}
public long getId() {
return Id;
}
public void setId(long id) {
Id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getLocation() {
return Location;
}
public void setLocation(String location) {
Location = location;
}
}
import java.util.ArrayList;
import java.util.List;
public class Repo<T, U> {
List<T> list = new ArrayList<T>();
public List<T> getAll() {
return list;
}
public void insert(T obj) {
list.add(obj);
}
public T get(U id) throws NoSuchMethodException, SecurityException {
for (T ele : list) {
if (ele.getClass().getMethod("getId") == id) {
return ele;
}
}
return null;
}
public void delete(U id) throws NoSuchMethodException, SecurityException {
list.remove(get(id));
}
}
public class DepartmentRepo extends Repo<Department, Long>{
}
class MainApi
{
public static void main (String[] args)
{
DepartmentRepo dept = new DepartmentRepo ();
Department ict=new Department(10001,"Dept of ICT","Town");
Department cs=new Department(10002,"Dept of Computer Science","Pampaimadu");
Department bio=new Department(10003,"Dept of Bio Science","Pampaimadu");
Department sats=new Department(10004,"Dept of Statistics","Kurumankadu");
dept.insert(ict);
dept.insert(cs);
dept.insert(bio);
dept.insert(sats);
System.out.println();
dept.getAll();
try{
dept.get(10001);
}
catch(Exception e){
}
}
}
As another solution, and an answer to your comment, you could use elements inheritance, and avoid reflection calls and exceptions.
1- Create an element interface OR class
public interface RepoElement<U> {
U getId();
}
OR
public class RepoElement<U> {
private U Id;
public RepoElement() {}
public RepoElement(U id) {
Id = id;
}
public U getId() {
return Id;
}
public void setId(U id) {
Id = id;
}
}
2- Make Department inherit from the interface OR class
public class Department implements RepoElement<Long> {
(...)
public Long getId() {
return Id;
}
(...)
}
OR
public class Department extends RepoElement<Long> {
private String Name;
private String Location;
public Department() {
super();
}
public Department(long id, String name, String location) {
super(id);
Name = name;
Location = location;
}
}
3- Use it directly in the Repo class (and remove exceptions)
public class Repo<T extends RepoElement<U>, U> {
(...)
public T get(U id) {
for (T ele : list) {
if (ele.getId().equals(id)) {
return ele;
}
}
return null;
}
public void delete(U id) {
list.remove(get(id));
}
(...)
}
As a last suggestion, you could use a Map instead of a List in the Repo class, and get rid of any search complexity/optimizations:
public class Repo<T extends RepoElement<U>, U> {
Map<U, T> map = new HashMap<U, T>();
public Collection<T> getAll() {
return map.values();
}
public void insert(T obj) {
map.put(obj.getId(), obj);
}
public T get(U id) {
return map.get(id);
}
public void delete(U id) {
map.remove(id);
}
}
You need to invoke the getId() method so that it will return the id to perform comparison correctly:
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
public class Repo<T, U> {
List<T> list = new ArrayList<T>();
public List<T> getAll() {
return list;
}
public void insert(T obj) {
list.add(obj);
}
public T get(U id) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
for (T ele : list) {
if (ele.getClass().getMethod("getId").invoke(ele).equals(id)) {
return ele;
}
}
return null;
}
public void delete(U id) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
list.remove(get(id));
}
}

Java hibernate conditionally apply annotation to a field?

Scenario: A data object which persists in the DB table. There are some old entries in the table. Now I have to apply encryption to new further entries in the table. So I add a new column which has the field encrypted set to False by default to check if the values are encrypted.
Problem: I want to write an annotation to encrypt the fields in the data model(POJO) before persisting and decrypt on getter() calls only if it is encrypted.
Context:
The user model.
public class UserData {
#Id
#Column(name = "ID", length = 36)
private String id;
#Column(name = "IS_ENCRYPTED")
private boolean isEncrypted;
#Column(name = "NAME")
#Convert(converter = EncryptionConverter.class)
private String name;
// more fields ....
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
// more similar getter and setters
}
The encryption class that i have written.
#Converter
public class EncryptionConverter implements AttributeConverter<String, String>{
private final String secretKey= "someSecret";
UserData Data = new UserData();
#Override
public String convertToDatabaseColumn(String str) {
if(!isNullOrBlank(str))
return AesEncrypt.encrypt(str, secretKey);
return str;
}
#Override
public String convertToEntityAttribute(String encrypedStr) {
if(!isNullOrBlank(encrypedStr) && Data.isEncrypted)
return AesEncrypt.decrypt(encrypedStr, secretKey);
return encrypedStr;
}
}
This class is inside the model class. (can move outside, but how to pass isencrypted flag to annotation)
How can I do this, is my approach correct?
Edit: there are multiple fields which are to be encrypted/decrypted not just name.
You can create the encryption behaviour in another configuration class, say EncryptedPropertyConfig, in this you can create a bean, EncryptablePropertyResolver from jasypt-spring-boot
#EnableAutoConfiguration
public class EncryptedPropertyConfig {
public EncryptedPropertyConfig() {
}
#Bean
public EncryptablePropertyResolver encryptablePropertyResolver() {
EncryptablePropertyResolver r = new MyPropertyPlaceholderConfigurer();
return r;
}
}
public final class MyPropertyPlaceholderConfigurer implements EncryptablePropertyResolver {
private StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
private EnvironmentStringPBEConfig envConfig = new EnvironmentStringPBEConfig();
public MyPropertyPlaceholderConfigurer() {
// set the encryption key and config
}
public String resolvePropertyValue(String passedValue) {
if (!PropertyValueEncryptionUtils.isEncryptedValue(passedValue)) {
return passedValue;
} else {
String returnValue = "";
try {
returnValue = PropertyValueEncryptionUtils.decrypt(passedValue, this.encryptor);
return returnValue;
} catch (Exception var4) {
throw new RuntimeException("Error in decryption of property value:" + passedValue, var4);
}
}
}
}
I suggest alternative solution using Entity Listeners
import javax.persistence.PostLoad;
import javax.persistence.PreUpdate;
public class UserData {
private final String secretKey= "someSecret";
// ...
#PreUpdate
private void onUpdate() {
// triggered before saving entity to DB (both create & update)
if(!isNullOrBlank(name)) {
name = AesEncrypt.encrypt(name, secretKey);
}
}
#PostLoad
private void onLoad() {
// triggered after entity is fetched from Entity Provider
if (!isNullOrBlank(name) && isEncrypted) {
name = AesEncrypt.decrypt(name, secretKey);
}
}
}
Instead of using JPA AttributeConverter you can implement hibernate user type in this way:
import java.util.Objects;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.StringType;
import org.hibernate.usertype.UserType;
public class CustomNameType implements UserType
{
private String secretKey = "someSecret";
public CustomNameType()
{
}
#Override
public Object deepCopy(Object value) throws HibernateException
{
if (null == value) return null;
return ((CustomName) value).clone();
}
#Override
public Object assemble(Serializable cached, Object owner) throws HibernateException
{
return cached;
}
#Override
public Serializable disassemble(Object value) throws HibernateException
{
return (Serializable) value;
}
#Override
public Object replace(Object original, Object target, Object owner) throws HibernateException
{
return original;
}
#Override
public boolean equals(Object one, Object two) throws HibernateException
{
return Objects.equals(one, two);
}
#Override
public int hashCode(Object obj) throws HibernateException
{
return Objects.hashCode(obj);
}
#Override
public boolean isMutable()
{
return true;
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
throws HibernateException, SQLException
{
boolean isEncrypted = rs.getBoolean(0); // IS_ENCRYPTED
String name = rs.getString(1); // NAME
if (isEncrypted) {
name = AesEncrypt.decrypt(name, secretKey);
}
return new CustomName(isEncrypted, name);
}
#Override
public void nullSafeSet(PreparedStatement statement, Object value, int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException
{
CustomName customName = (CustomName) value;
String name = customName.getName();
if (customName.isEncrypted()) {
name = AesEncrypt.encrypt(name, secretKey);
}
statement.setBoolean(0, customName.isEncrypted());
statement.setString(1, name);
}
#Override
public Class<?> returnedClass()
{
return CustomName.class;
}
#Override
public int[] sqlTypes()
{
// I do not know the types of your IS_ENCRYPTED and NAME fields
// So, this place maybe require correction
int[] types = {BooleanType.INSTANCE.sqlType(), StringType.INSTANCE.sqlType()};
return types;
}
}
where CustomName is:
public class CustomName implements Serializable, Cloneable
{
private boolean isEncrypted;
private String name;
public CustomName(boolean isEncrypted, String name)
{
this.isEncrypted = isEncrypted;
this.name = name;
}
// getters , equals, hashCode ...
#Override
public CustomName clone()
{
return new CustomName(isEncrypted, name);
}
}
and then use it:
import org.hibernate.annotations.Type;
import org.hibernate.annotations.Columns;
#Entity
public class UserData {
#Type(type = "com.your.CustomNameType")
#Columns(columns = {
#Column(name = "IS_ENCRYPTED"),
#Column(name = "NAME")
})
private CustomName name;
}

How to get access to a HashMap of objects from the Objects in that HashMap. (Java)

I have a hash map of some POJO Objects of a Class named User: HashMap<ObjectId, User>
These objects (Users) are related to each other. (I need to search through other users to Update one's Parameters)
How can I have access to the HashMap within a user object?
import org.bson.types.ObjectId;
import org.bson.BsonDocument;
import java.util.ArrayList;
import java.util.List;
public class User {
private ObjectId _id;
private int grade;
private String region;
private ArrayList<ObjectId> _reg_by;
private ObjectId regBy;
public User(){
}
public ObjectId getId() {
return _id;
}
public void setId(final ObjectId id) {
this._id = id;
}
public int getGrade() {
return grade;
}
public void setGrade(final int grade) {
this.grade = grade;
}
public String getRegion() {
return region;
}
public void setRegion(final String region) {
this.region = region;
}
public ObjectId getRegBy() {
if(regBy == null) {
regBy = ((_reg_by.size() != 0) ? _reg_by.get(0) : null);
}
return regBy;
}
public void setRegBy(final ObjectId regBy) {
this.regBy = regBy;
}
public ArrayList<ObjectId> get_reg_by(){
return _reg_by;
}
public void set_reg_by(ArrayList<ObjectId> _reg_by){
this._reg_by = _reg_by;
}
private String updateRegion(){
if(getRegBy() == null)
return null;
//TODO search for the person who registered him and use the region!
// how to get access to others from here?!
}
}
This is the User class where in regionUpdate() function I want to have this access
I creat this HashMap in my Main function.
HashMap<ObjectId, User> users = mongoHandler.getUsers();
I solved my problem by defining my HashMap as Static.
public static HashMap<ObjectId, User> users
so I can easily have access to it from anywhere by using the following code:
HashMap<ObjectId, User> users = Main.users
or any method e.g. Main.users.getId();
Another solution could have been to create a property within your "User" class that contains a list of related users, and if you know that one user is related to another, added it to the each user as you build the list.
public class User {
...
private List<User> relatedUsers = new ArrayList<User>();
...
private void updateRelatedUsers() {
for(User relatedUser : relatedUsers) {
//do stuff to update the relatedUser object.
relatedUser.setSomething(someValue);
}
}
//Getter and setter
public List<User> getRelatedUsers() {
return relatedUsers;
}
public void setRelatedUsers(List<User> relatedUsers) {
this.relatedUsers = relatedUsers;
}
...
}
Add the users like so:
...
User myUser = creatUserHoweverYouDo();
User myRelatedUser = getMyRelatedUser(myUser);
myUser.getRelatedUsers().add(myRelatedUser);
...

How to get an instance of #Component in Spring?

I've a #Container class called RegistrationValidator that depends on a spring #Repository. Getting an instance of it in a service or another container through autowiring is fine, but I need access to it in a regular POJO. How can I get an instance of this validator without converting all POJOs to #Container or #Service objects. Is there a way to add a static getInstance method within the RegistrationValidator that returns an instance.
package core.dao.validator;
import core.dao.Registration;
import core.repository.RegistrationRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
/**
* Validates the Registration DAO.
*
* #author Srini Katragadda
*/
#Component
public class RegistrationValidator implements Validator {
private static final Logger LOGGER = LoggerFactory.getLogger(RegistrationValidator.class);
private static final int MAX_SIZE = 255;
private final RegistrationRepository repository;
#Autowired
RegistrationValidator(RegistrationRepository repository) {
this.repository = repository;
}
#Override
public boolean supports(Class clazz) {
return Registration.class.equals(clazz);
}
#Override
public void validate(Object obj, Errors errors) {
Registration registration = (Registration) obj;
// Validate Message Id
ValidationUtils.rejectIfEmpty(errors, "messageId", "messageId.empty");
if (registration.getMessageId() != null &&
registration.getMessageId().length() > MAX_SIZE)
errors.rejectValue("messageId", "Size greater than " + MAX_SIZE);
// Validate for duplicate messageId information
if (hasDuplicateMessageIdInDatabase(registration)) {
errors.rejectValue("callouts", "Database has duplicate registration with messageId.");
}
}
/**
* Check if there is duplicate messageId in database.
*/
private boolean hasDuplicateMessageIdInDatabase(Registration adsRegistration) {
return (repository.findByMessageId(adsRegistration.getMessageId()) != null);
}
}
Here is where I need an instance of that validator. Checkout the Builder.build() method which is passing an instance of validator to a utility method. This worked fine until I needed to autowire a #Repository in RegistrationValidator. Now I need to pass the instance of repository to be able to construct the validator and was wondering how without making the DAO itself another component and autowiring it with RegistrationValidator.
package core.dao;
import core.dao.validator.RegistrationValidator;
import core.util.ValidatorUtil;
import core.util.ToString;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* The data access object that holds the information of an Registration.
*/
#JsonIgnoreProperties(ignoreUnknown = true)
public final class Registration {
private String id;
private String messageId;
private String version;
public Registration() {
}
private Registration(Builder builder) {
this.id = builder.id;
this.messageId = builder.messageId;
this.version = builder.version;
}
public static Builder getBuilder() {
return new Builder();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getMessageId() {
return messageId;
}
public void setMessageId(String messageId) {
this.messageId = messageId;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
/**
* Builder pattern makes the object easier to construct in one line.
*/
public static class Builder {
private String id;
private String messageId;
private String version;
private Builder() {}
public Builder id(String id) {
this.id = id;
return this;
}
public Builder messageId(String messageId) {
this.messageId = messageId;
return this;
}
public Builder version(String version) {
this.version = version;
return this;
}
public Registration build() {
Registration entry = new Registration(this);
return (Registration) ValidatorUtil.validate(entry, new RegistrationValidator());
}
}
}
Here is ValidatorUtil.validate code for completeness.
public static Object validate(Object entry, org.springframework.validation.Validator customValidator) {
Errors errors = new BeanPropertyBindingResult(entry, entry.getClass().getName());
customValidator.validate(entry, errors);
if (errors == null || errors.getAllErrors().isEmpty())
return entry;
else {
LOGGER.error(errors.toString());
throw new InvalidDataException(errors.getAllErrors().toString(), errors);
}
}
You could autowire spring bean into pojo with the SpringBeanAutowiringSupport, in the following way:
public class Pojo {
#Autowired
private RegistrationRepository repository
public void methodWhichRequiresBean() {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
repository.doStuff()
}
Hope this helps

Categories