Populate #Transient field within Spring Data Jpa Repository - spring

Is possible to populate transient field in entity class with Spring Data REST api somehow (by projection or something) - to get that value in JSON response ? I need to populate for example info field with value got from second datasource (i had Spring repo bean for this datasource and need inject it in something like "interceptor" and fill that field).
#Entity
public class User {
#Id
private Long id;
#Transient
private String info;
// getters & setters
}
public interface UserRepository extends JpaRepository<User, Long> {
}

I found solution using PostLoadEventListener, but it is for Hibernate, not exactly what i was looking for, but works. I think it should be more general Spring-ly solution.
#Component
public class UserInterceptor implements PostLoadEventListener {
#Autowired
private SecondRepository repo;
#Autowired
#Qualifier("prmiaryEntityManagerFactory")
private EntityManagerFactory entityManagerFactory;
#PostConstruct
private void init() {
HibernateEntityManagerFactory hibernateEntityManagerFactory = (HibernateEntityManagerFactory) this.entityManagerFactory;
SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) hibernateEntityManagerFactory.getSessionFactory();
EventListenerRegistry registry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
registry.appendListeners(EventType.POST_LOAD, this);
}
#Override
public void onPostLoad(PostLoadEvent event) {
final Object entity = event.getEntity();
if(entity != null && entity instanceof User) {
User user = (User) entity;
// populate using another repo bean
Info s = repo.findOne(user.getInfoId());
user.setInfo(s.getName());
}
}
}

Related

How to exclude/disable #Entity Annotation for particular class

I want to disable #Entity Annotation for particular class.
Here is my sample code.
#Component
public class GenericDropDown{
private Integer id;
private String key;
private String value;
// Standard getter and setter
The above class is used for fetching data from multiple table for rendering different dropdown list from different tables.
How I can achieve this without #Entity Annotation
Here is my sample code.
#Component
public class GenericDropDown{
private Integer id;
private String key;
private String value;
// Standard getter and setter
#Repository
public class DropDownDao {
#Autowired
private EntityManager entityManager;
public Object runNativeQuery() {
#SuppressWarnings("unchecked")
List<Priority> o= entityManager.createNativeQuery("select Id,PRKEY,PRVALUE from Priority",Priority.class)
.getResultList();
return o;
}
}
**Error:**Unknown entity: com.min.test.Project.entity.Priority; nested exception is org.hibernate.MappingException: Unknown entity: com.min.test.Project.entity.Priority
You can select List of Objects array and map them yourself.
List<Object[]> o = entityManager.createNativeQuery("select Id,PRKEY,PRVALUE from Priority").getResltList();
List<MyClass> result = o.stream().map(arr -> new MyClass((Long) arr[0], (String) arr[1])).collect(Collectors.toList());
Or you also can use a JdbcTemplate instead of EntityManager:
#Autowired
private JdbcTemplate jdbcTemplate;
public List<MyClass> runQuery() {
String select = "select Id,yourParameterHere from Priority";
return jdbcTemplate.query(select, (rs, rowNum) -> new MyClass(rs.getLong("Id"), rs.getString("yourParameterHere")));
}

Mybatis dao pattern not autowiring field

Trying to make a dao pattern for mybatis and spring. Want to use this sql queries anywhere i want, just using dependency injection.
When i try to use this method (.getMaxId()) it gives me "Null pointer exception".
Why field SqlSession is not autowiring(gives null)? Intellige idea shows this field as a canditate for
autowiring.
I think there is 3 steps to achieve:
1) Autowire session
2) get mapper from session
3) execute queries from this mapper
I do this
#Autowired
private Student_mapper sm;
sm.getMaxId();
Service
#Service
#Transactional
public class Student_mapperImpl {
#Autowired
private SqlSession session;
#Autowired
Student_mapper mapper = session.getMapper(Student_mapper.class);
public Integer getMaxId() {
Integer value = mapper.getMaxId();
return value;
}
}
Bean configuration file
#org.springframework.context.annotation.Configuration
#EnableTransactionManagement
#ComponentScan
public class DataSourceStudent_Mapper {
#Bean
public SqlSession getDataSource() {
String user = "postgres";
String password = "postgres";
String databasenameURL = "jdbc:postgresql://localhost:5432/postgres";
String dbDriver = "org.postgresql.Driver";
DataSource dataSource = new org.apache.ibatis.datasource.pooled.PooledDataSource(
dbDriver, databasenameURL, user, password);
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development",
transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(configuration);
SqlSession session = sqlSessionFactory.openSession();
session.getConfiguration().addMapper(Student_mapper.class);
return session;
}
}
Student_mapper - interface with a queries
#Repository
public interface Student_mapper {
#Select("select max(id) from student")
#Result(property = "id", column = "ID")
Integer getMaxId();
}
Entity
public class Student {
private int id;
private String name;
private String branch;
private int percentage;
private int phone;
private String email;
//(setters,getters, allArgs constructor are ommited)
}
I don't understand what's wrong. There is any examples how to realise this? I would like to execute my queries anywhere i want without constantly initializing the session, datasource etc. Thanks in advance
mybatis-spring Getting Started should be useful.

Select one column using Spring Data JPA

Does anyone have any idea how to get a single column using Spring Data JPA? I created a repository like below in my Spring Boot project, but always get the {"cause":null,"message":"PersistentEntity must not be null!"} error when accessing the Restful URL.
#RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UsersRepository extends CrudRepository<Users, Integer> {
#Query("SELECT u.userName FROM Users u")
public List<String> getUserName();
}
Then if I access the Restful URL like ../users/search/getUserName, I get the error:
{"cause":null,"message":"PersistentEntity must not be null!"}
Create a Projection interface
public interface UserNameOnly {
String getUserName();
}
Then in your repository interface return that type instead of the user type
public interface UserRepository<User> extends JpaRepository<User,String> {
List<UsernameOnly> findNamesByUserNameNotNull();
}
The get method in the projection interface must match a get method of the defined type on the JPA repository, in this case User.
The "findBySomePropertyOnTheObjectThatIsNotNull" allows you to get a List of the entities (as opposed to an Iterable) based on some criteria, which for a findAll can simply be if the unique identifier (or any other NonNull field) is not null.
Concept is : In your entity class create a constructor with only required instant variables. And use that constructor in the repository method shown below.
Lets say you have a interface Repository like below
Repository implementation:
public interface UserRepository<User> extends JpaRepository<User,String>
{
#Query(value = "select new com.org.User(usr.userId) from User usr where usr.name(:name)")
List<User> findUserIdAlone(#Param("name") String user);
}
In Controller
#RestController
public class UserController
{
#Autowired
private UserRepository<User> userRepository;
#Res
public ResponseEntity<User> getUser(#PathVariable("usrname") String userName)
{
User resultUser = usrRepository.findUserIdAlone(userName);
return ResponseEntity.ok(resultUser);
}
}
public class User
{
private String userId,userName;
public User(String userId)
{
this.userId=userId;
}
// setter and getters goes here
}
This Works for me.
public interface UserDataRepository extends JpaRepository<UserData, Long> {
#Query(value = "SELECT emp_name FROM user_data", nativeQuery = true)
public List<Object[]> findEmp_name();
}
System.out.println("data"+ userDataRepository.findEmp_name());
The above line gave me this result :
data[abhijeet, abhijeet1, abhijeet2, abhijeet3, abhijeet4, abhijeet5]
If you want to only return a single column you should look at Projections and Excerpts which will allow you to filter specific columns and other things that are usefule.
If you need list all of the users, try select userName from Users, if you need one user use "where" look at spring data JPA http://docs.spring.io/spring-data/jpa/docs/current/reference/html/ , try change CrudRepository to JpaRepository
It is possible to provide custom implementations of methods in a Spring Data JPA repository, which enables complete control on queries and return types. The approach is as follows:
Define an interface with the desired method signatures.
Implement the interface to achieve the desired behavior.
Have the Repository extend both JpaRepository and the custom interface.
Here is a working example that uses JpaRepository, assuming a user_table with two columns, user_id and user_name.
UserEntity class in model package:
#Entity
#Table(name = "user_table")
public class UserEntity {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "user_id")
private Long userId;
#Column(name = "user_name")
private String userName;
protected UserEntity() {}
public UserEntity(String userName) {
this.userName = userName;
// standard getters and setters
}
Define interface for the custom repository in the repository package:
public interface UserCustomRepository {
List<String> findUserNames();
}
Provide implementation class for the custom interface in the repository package:
public class UserCustomRepositoryImpl implements UserCustomRepository {
// Spring auto configures a DataSource and JdbcTemplate
// based on the application.properties file. We can use
// autowiring to get a reference to it.
JdbcTemplate jdbcTemplate;
#Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
// Now our custom implementation can use the JdbcTemplate
// to perform JPQL queries and return basic datatypes.
#Override
public List<String> findUserNames() throws DataAccessException {
String sql = "SELECT user_name FROM user_table";
return jdbcTemplate.queryForList(sql, String.class);
}
}
Finally, we just need to have the UserRepository extend both JpaRepository and the custom interface we just implemented.
public interface UserRepository extends JpaRepository<UserEntity, Long>, UserCustomRepository {}
Simple test class with junit 5 (assuming the database is initially empty):
#SpringBootTest
class UserRepositoryTest {
private static final String JANE = "Jane";
private static final String JOE = "Joe";
#Autowired
UserRepository repo;
#Test
void shouldFindUserNames() {
UserEntity jane = new UserEntity(JANE);
UserEntity joe = new UserEntity(JOE);
repo.saveAndFlush(jane);
repo.saveAndFlush(joe);
List<UserEntity> users = repo.findAll();
assertEquals(2, users.size());
List<String> names = repo.findUserNames();
assertEquals(2, names.size());
assertTrue(names.contains(JANE));
assertTrue(names.contains(JOE));
}
}

Resolving entities with Spring Data Neo4j returns wrong entity types

I'm experiencing some strange behavior when I'm looking up node entities with Spring Data Neo4j (SDN). If I use GraphRepository.findOne(long) it will return an entity with that identifier even though the entity is not of the same type.
This is what my (very) simplified entity structure looks like:
#NodeEntity
protected abstract class BaseEntity {
#GraphId
private Long id;
#JsonIgnore
#RelatedTo(type = RelationType.ENTITY_AUDIT)
private Audit audit;
}
#NodeEntity
public final class Person extends BaseEntity {
#Indexed(indexType = IndexType.FULLTEXT)
private String firstName;
#Indexed(indexType = IndexType.FULLTEXT)
private String lastName;
}
#NodeEntity
public class Audit extends BaseEntity {
#RelatedTo(type = RelationType.ENTITY_AUDIT, direction = Direction.INCOMING)
private BaseEntity parent;
private Long date;
private String user;
}
For every entity type, I've created repositories like this:
#Repository
public interface PersonRepository extends GraphRepository<Person> {}
#Repository
public interface AuditRepository extends GraphRepository<Audit> {}
I've got an abstract base class for my service layer classes. That is what they roughly look like:
public abstract class MyServiceImpl<T extends BaseEntity> implements MyService<T> {
private GraphRepository<T> repository;
public MyServiceImpl(final GraphRepository<T> repository) {
this.repository = repository;
}
#Override
public T read(final Long identifier) throws EntityNotFoundException {
return repository.findOne(identifier);
}
#Override
public T create(final T entity) {
return repository.save(entity);
}
}
#Service
public class PersonServiceImpl extends MyServiceImpl<Person> implements PersonService {
private PersonRepository personRepository;
#Autowired
public PersonServiceImpl(final PersonRepository personRepository) {
super(personRepository);
this.personRepository = personRepository;
}
}
When I execute the following code, the result is not as expected:
Person person = new Person();
person.setFirstName("Test");
person.setLastName("Person");
personService.create(person);
// suppose the person identifier is 1L
final Audit audit = auditRepository.findOne(1L);
You'd expect that the AuditRepository would return null, but this in not the case. Instead, it returns an Audit with identifier 1L and null in all of its properties. It seems that as long as there's a node that corresponds to a given identifier, it will be returned, no mather what its type is. If Person and Audit would have had matching property names, they would contain their values too... Is all this expected behavior, or am I missing something?
For now, I've solved this problem with the code below, where I do the type check myself.
public abstract class MyServiceImpl<T extends BaseEntity> implements MyService<T> {
private GraphRepository<T> repository;
public MyServiceImpl(final GraphRepository<T> repository) {
this.repository = repository;
}
#Override
public T read(final Long identifier) throws EntityNotFoundException {
return get(identifier);
}
protected T get(final Long identifier) throws EntityNotFoundException {
final T entity = repository.findOne(identifier);
final Class<T> type = getServiceType();
if (entity == null || !(type.equals(repository.getStoredJavaType(entity)))) {
throw new EntityNotFoundException(type, identifier);
}
return entity;
}
#SuppressWarnings("unchecked")
private Class<T> getServiceType() {
return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
If you need more configuration, please let me know.
My framework versions are:
<spring.version>3.2.0.RC1</spring.version>
<neo4j.version>1.8</neo4j.version>
<spring.data.neo4j.version>2.1.0.RELEASE</spring.data.neo4j.version>
we had that behavior before that it failed on the wrong entity type being returned, we changed that behavior so that the type you provide is used to automatically project the node to.
public <S extends PropertyContainer, T> T createEntityFromStoredType(S state, MappingPolicy mappingPolicy) {..}
template. createEntityFromStoredType(node, null) will get you the object with the stored state.
public Class getStoredJavaType(Object entity) {}
gives you the stored class for a node or relationship (or entity)
We had a discussion of changing the behavior back and failing esp. in Repositories.
The question is, what should happen then? An Exception? A Null result? ...
In general if you provide a raw node-id that is valid, returning an error or Null doesn't seem to be like a correct answer either?

Spring Data Neo4J #Indexed(unique = true) not working

I'm new to Neo4J and I have, probably an easy question.
There're NodeEntitys in my application, a property (name) is annotated with #Indexed(unique = true) to achieve the uniqueness like I do in JPA with #Column(unique = true).
My problem is, that when I persist an entity with a name that already exists in my graph, it works fine anyway.
But I expected some kind of exception here...?!
Here' s an overview over basic my code:
#NodeEntity
public abstract class BaseEntity implements Identifiable
{
#GraphId
private Long entityId;
...
}
public class Role extends BaseEntity
{
#Indexed(unique = true)
private String name;
...
}
public interface RoleRepository extends GraphRepository<Role>
{
Role findByName(String name);
}
#Service
public class RoleServiceImpl extends BaseEntityServiceImpl<Role> implements
{
private RoleRepository repository;
#Override
#Transactional
public T save(final T entity) {
return getRepository().save(entity);
}
}
And this is my test:
#Test
public void testNameUniqueIndex() {
final List<Role> roles = Lists.newLinkedList(service.findAll());
final String existingName = roles.get(0).getName();
Role newRole = new Role.Builder(existingName).build();
newRole = service.save(newRole);
}
That's the point where I expect something to go wrong!
How can I ensure the uniqueness of a property, without checking it for myself??
P.S.: I'm using neo4j 1.8.M07, spring-data-neo4j 2.1.0.BUILD-SNAPSHOT and Spring 3.1.2.RELEASE.
I walked into the same trap... as long as you create new entities, you will not see the exception - the last save()-action wins the battle.
Unfortunately, the DataIntegrityViolationException will be raised only in case of update an existing entity!
A detailed description of that behaviour can be found here:
http://static.springsource.org/spring-data/data-graph/snapshot-site/reference/html/#d5e1035
If you are using SDN 3.2.0+ use the failOnDuplicate attribute:
public class Role extends BaseEntity
{
#Indexed(unique = true, failOnDuplicate = true)
private String name;
...
}

Resources