Folks,
We have really hard time in integrating auditing support in Spring DATA JPA.
We are using spring 3.2.11 and hibernate 4.3.0. (We don't want to use ORM.xml)
Problem is we are not receiving any event in the 'AuditAwareImpl' class when customer entity is saved.
(We debugged AuditingEntityListener and found AuditHandler is getting injected properly but at later point event is not fired. We suspect some class loading issue)
CODE below.
Basically we have two business entity.
#Entity(name = "CUSTOMER")
#Table(name = "CUSTOMER_DETAILS")
#EntityListeners(AuditingEntityListener.class)
public class Customer extends AbstractAuditable<User, Long> {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
User Entity :
#Entity(name = "USER")
#Table(name = "USER_DETAILS")
public class User extends AbstractPersistable<Long>{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
We have one helper class to configure spring.
#Configuration
#EnableTransactionManagement
#EnableJpaAuditing(auditorAwareRef = "auditorAwareImpl")
#EnableJpaRepositories(basePackages = "businessclass")
public class PersistenceContext {
}
AuditAwareImpl.
#Component
public class AuditorAwareImpl implements AuditorAware<User> {
public User getCurrentAuditor() {
ApplicationContext context = ApplicationContextManager.getContext();
UserRepository repository = (UserRepository)context.getBean("userRepository");
User user = new User();
user.setName("work now");
repository.save(user);
return user;
}
}
You need to call the repository's saveAndFlush method.
The save method of org.springframework.data.jpa.repository.JpaRepository supports both save and update. If it is update, it calls em.merge (), but it does not trigger the PreUpdate event. If you want to trigger PreUpdate, you Need to call the saveAndFlush method
Related
My Film model explicitly states it should fetch its children Actors lazily.
#Entity
#Getter
#Setter
public class Film {
...
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "film_actor",
joinColumns = #JoinColumn(name = "film_id"),
inverseJoinColumns = #JoinColumn(name = "actor_id")
)
private List<Actor> cast = new ArrayList<>();
This actually works perfectly when using the service/repository in any other context:
#Override
public void run(String... args) throws Exception {
List<Film> films = filmService.getAllFilms();
System.out.println(films);
}
But then for some mysterious reason, ONLY when used in a Spring MVC controller method (using typical annotations like #Controller and #RequestMapping), it always comes back eagerly loaded...Why and how do I change this? If I have 1000 films I want to display at once, I don't want to load in a million actors!
#GetMapping("")
public String filmsPage(Model model){
List<Film> allMyFilms = filmService.getAllFilms();
model.addAttribute("films", allMyFilms);
return "film/film-list";
}
Incidentally, for completeness, here are my service/repository layers:
#Service
#RequiredArgsConstructor
public class FilmServiceImpl implements FilmService {
private final FilmRepository filmRepo;
...
#Override
public List<Film> getAllFilms() {
List<Film> films = filmRepo.findAll();
return films;
}
Repository layer:
#Repository
public interface FilmRepository extends JpaRepository<Film, Long> {
List<Film> findAll();
}
How do you verify that the association is eager? Spring MVC has something enabled by default which is called "open session in view", which allows lazy loading until the request is finished. If you "check" whether data is loaded through your debugger, the debugger will invoke the toString method of PersistentBag which will initialize the lazy collection.
I have several application-x.properties files which are used for different profiles and each file contains a property with a specific value for each profile. I want to use this property in queries to database as a parameter.
Is it possible to add it using SpEL or something else?
For instance application.properties:
query.parameters.role: ADMIN
possible usage:
#Query(value = "select u from User u where u.role = :#{query.parameters.role}")
Set<User> getAllUsers();
You might do it by following way:
1.- Find all users by role using Repository Query Keywords
#Repository
public interface UserRepository extends JpaRepository<User, UUID> {
Set<User> findByRole(String role);
}
2.- Create a method called getAllUsers in UserService and get role value by using #Value:
#Service
public class UserService {
#Autowired
private UserRepository repository;
#Value("${query.parameters.role}")
private String role;
public Set<User> getAllUsers() {
return repository.findByRole(role);
}
}
Other way to answer to this question is implement a custom SpEL that is supported by #Query you can take a look this SpEL support in Spring Data JPA
Then you should follow these steps for your case:
1.- Create a ConfigProperties class so that you can read and get the application.properties.
#Configuration
#PropertySource("classpath:application.properties")
#ConfigurationProperties(prefix = "query")
public class ConfigProperties {
private Parameters parameters;
// standard getters and setters
}
public class Parameters {
private String role;
// standard getters and setters
}
2.- Implement a custom EvaluationContextExtensionSupport and reference to ConfigProperties
public class PropertyEvaluationContextExtension extends EvaluationContextExtensionSupport {
private final ConfigProperties configProperties;
public PropertyEvaluationContextExtension(final ConfigProperties configProperties) {
this.configProperties= configProperties;
}
#Override
public String getExtensionId() {
return "properties";
}
#Override
public ConfigProperties getRootObject() {
return this.configProperties;
}
}
3.- Create a bean in order to be called your custom PropertyEvaluationContextExtension
#Configuration
public class CustomConfig {
private final ConfigProperties configProperties;
public CustomConfig(final ConfigProperties configProperties) {
this.configProperties= configProperties;
}
#Bean
EvaluationContextExtensionSupport propertyExtension() {
return new PropertyEvaluationContextExtension(configProperties);
}
}
4.- Call the query.parameters.role by following format: ?#{query.parameters.role}
#Query(value = "SELECT u FROM User u WHERE u.role = ?#{query.parameters.role}")
Set<User> getAllUsers();
I am working on spring boot application with RestController, Service a Repository and an Entity.
My problem is when I call the web service to save my data in the data base, it seems it works fine and there is no exception thrown but when I check my data base I find that the table was created but I find no data saved. and here is what I get in the output(for each element in my list):
Hibernate:
insert
into
table_name
(columnOne, columnTwo)
values
(?, ?)
Here is my code:
RestController:
#RestController
#RequestMapping(path = "/api/")
public class myController {
#Autowired
private MyService myService;
#PostMapping(path="/inject/{year}")
public void myControllerMethod(#PathParam("year") Year year) {
this.myService.myServiceMethod(year);
}
}
Service:
#Service
public class MyService {
#Autowired
MyRepository myRepository;
public void myServiceMethod(Year year) {
List<MyEntity> myEntityList = this.parseMyEntityList(year);
this.myRepository.save(myEntityList)
}
}
Repository:
#Repository
public interface MyRepository extends CrudRepository<MyEntity, Long>, JpaSpecificationExecutor<InseeLibelle> {
}
Entity:
#Entity
#Table(name = "table_name", indexes = {
#Index(name = "columnOne_idx", columnList = "columnOne"),
#Index(name = "columneTwo_idx", columnList = "columnTwo"),
})
public class MyEntity{
#JsonIgnore
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long columnId;
#Column
private Integer columnOne;
#Column
private String columnTwo;
public Integer getColumnOne() {
return columnOne;
}
public void setColumnOne(Integer columnOne) {
this.columneOne = colmunOne;
}
public String getColumnTwo() {
return columnTwo;
}
public void setColumnTwo(String columnTwo) {
this.columnTwo = columnTwo;
}
}
I tried to add this line in the repository but it does not work too:
<S extends MyEntity> Iterable<S> save(Iterable<S> entities) ;
Perhaps the problem is with the pgAdmin (like my case), it does not show the data but they exist in the database, try findAll method in the repository or check them with select * directly.
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));
}
}
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;
...
}