I am new to Spring and I am trying to create an Object and add it to my database and then get the value from it. As far as I understand I should not add any extra lines and findAll should return me a proper looking String as a result.
But the result I get looks like this:
[model.Orders#4a163575, model.Orders#7ecec90d]
What I also understood is that I should not add get/set methods to Spring as they should be automatically generated, but when I try to cast the model.Orders#4a163575 into an Object and do the get method It tells me that there is no get method.
So here is my Object:
#Data
#Entity
public class Orders {
public Orders(String orderName) {
this.orderName = orderName;
}
public Orders() {
}
#Id
#GeneratedValue
private Long id;
private String orderName;
}
Then the findAll method:
#Repository
public class OrderDao {
public List<Orders> findAll(){
return em.createQuery("select p from Orders p", Orders.class).getResultList();
}
}
And where I launch it all:
public static void main(String[] args) {
ConfigurableApplicationContext ctx =
new AnnotationConfigApplicationContext(DbConfig.class);
OrderDao dao = ctx.getBean(OrderDao.class);
dao.save(new Orders("order1"));
dao.save(new Orders("order2"));
System.out.println(dao.findAll());
}
From what I have I think that the #Data annotation is not working properly since there is no toString nor getters/setter.
I import the #Data annotation with this line : import lombok.Data;.
What am I doing wrong here.
You need to install lombok plugin for that it will be possible generating them
You can refer to this article how to install lombok in IntellijIdea:
Lombok annotations do not compile under Intellij idea
Please also add enable annotation processing
Well it seems I had forgotten to enable annotation processing.
Picture to show where and how I did it
Related
I would like to have Documents stored with an UUID id and createdAt / updatedAt fields. My solution was working with Spring Boot 2.1.x. After I upgraded from Spring Boot 2.1.11.RELEASE to 2.2.0.RELEASE my test for MongoAuditing failed with createdAt = null. What do I need to do to get the createdAt field filled again?
This is not just a testproblem. I ran the application and it has the same behaviour as my test. All auditing fields stay null.
I have a Configuration to enable MongoAuditing and UUID generation:
#Configuration
#EnableMongoAuditing
public class MongoConfiguration {
#Bean
public GenerateUUIDListener generateUUIDListener() {
return new GenerateUUIDListener();
}
}
The listner hooks into the onBeforeConvert - I guess thats where the trouble starts.
public class GenerateUUIDListener extends AbstractMongoEventListener<IdentifiableEntity> {
#Override
public void onBeforeConvert(BeforeConvertEvent<IdentifiableEntity> event) {
IdentifiableEntity entity = event.getSource();
if (entity.isNew()) {
entity.setId(UUID.randomUUID());
}
}
}
The document itself (I dropped the getter and setters):
#Document
public class MyDocument extends InsertableEntity {
private String name;
}
public abstract class InsertableEntity extends IdentifiableEntity {
#CreatedDate
#JsonIgnore
private Instant createdAt;
}
public abstract class IdentifiableEntity implements Persistable<UUID> {
#Id
private UUID id;
#JsonIgnore
public boolean isNew() {
return getId() == null;
}
}
A complete minimal example can be find here (including a test) https://github.com/mab/auditable
With 2.1.11.RELEASE the test succeeds with 2.2.0.RELEASE it fails.
For me the best solution was to switch from event UUID generation to a callback based one. With the implementation of Ordered we can set the new callback to be executed after the AuditingEntityCallback.
public class IdEntityCallback implements BeforeConvertCallback<IdentifiableEntity>, Ordered {
#Override
public IdentifiableEntity onBeforeConvert(IdentifiableEntity entity, String collection) {
if (entity.isNew()) {
entity.setId(UUID.randomUUID());
}
return entity;
}
#Override
public int getOrder() {
return 101;
}
}
I registered the callback with the MongoConfiguration. For a more general solution you might want to take a look at the registration of the AuditingEntityCallback with the `MongoAuditingBeanDefinitionParser.
#Configuration
#EnableMongoAuditing
public class MongoConfiguration {
#Bean
public IdEntityCallback registerCallback() {
return new IdEntityCallback();
}
}
MongoTemplate works in the following way on doInsert()
this.maybeEmitEvent - emit an event (onBeforeConvert, onBeforeSave and such) so any AbstractMappingEventListener can catch and act upon like you did with GenerateUUIDListener
this.maybeCallBeforeConvert - call before convert callbacks like mongo auditing
like you can see in source code of MongoTemplate.class src (831-832)
protected <T> T doInsert(String collectionName, T objectToSave, MongoWriter<T> writer) {
BeforeConvertEvent<T> event = new BeforeConvertEvent(objectToSave, collectionName);
T toConvert = ((BeforeConvertEvent)this.maybeEmitEvent(event)).getSource(); //emit event
toConvert = this.maybeCallBeforeConvert(toConvert, collectionName); //call some before convert handlers
...
}
MongoAudit marks createdAt only to new entities by checking if entity.isNew() == true
because your code (UUID) already set the Id the createdAt is not populated (the entity is not considered new)
you can do the following (order by best to worst):
forget about the UUID and use String for your id, let the mongo itself create and manage it's entities ids (this how MongoTemplate actually works lines 811-812)
keep the UUID at the code level, convert from/to String when inserting and retrieving from the db
create a custom repository like in this post
stay with 2.1.11.RELEASE
set the updateAt by GenerateUUIDListener as well as id (rename it NewEntityListener or smth), basically implement the audit
implement a new isNew() logic that don't depends only on the entity id
in version 2.1.11.RELEASE the order of the methods was flipped (MongoTemplate.class 804-805) so your code worked fine
as an abstract approach, the nature of event is to be sort of send-and-forget (async compatible), so it's a very bad practice to change the object itself, there is NO grantee for order of computation, if any
this is why the audit build on callbacks and not events, and that's why Pivotal don't (need to) keep order between versions
I am using Spring boot v2 with mongo database. I was wondering what is the best way to do partial updates on the data model. Say I have a model with x attributes, depending on the request I may only want to update 1, 2 , or x of them attributes. Should I be exposing an end point for each type of update operation, or is it possible to expose one end pint and do it in a generic way? Note I will need to be able to validate the contents of the request attributes (e.g tel no must be numbers only)
Thanks,
HTTP PATCH is a nice way to update a resource by specifying only the properties that have changed.
The following blog explain it very well
You can actually expose just one endpoint. This is the situation I had a few months ago:
I wanted people to modify any (or even all)fields of a Projects document (who am I to force the users to manually supply all fields lol). So I have my Model,
Project.java:
package com.foxxmg.jarvisbackend.models;
//imports
#Document(collection = "Projects")
public class Project {
#Id
public String id;
public String projectTitle;
public String projectOverview;
public Date startDate;
public Date endDate;
public List<String> assignedTo;
public String progress;
//constructors
//getters & setters
}
I have my repository:
ProjectRepository.java
package com.foxxmg.jarvisbackend.repositories;
//imports
#Repository
public interface ProjectRepository extends MongoRepository<Project, String>, QuerydslPredicateExecutor<Project> {
//please note, we are going to use findById(string) method for updating
Project findByid(String id);
//other abstract methods
}
Now to my Controller, ProjectController.java:
package com.foxxmg.jarvisbackend.controllers;
//import
#RestController
#RequestMapping("/projects")
#CrossOrigin("*")
public class ProjectController {
#Autowired
private ProjectRepository projectRepository;
#PutMapping("update/{id}")
public ResponseEntity<Project> update(#PathVariable("id") String id, #RequestBody Project project) {
Optional<Project> optionalProject = projectRepository.findById(id);
if (optionalProject.isPresent()) {
Project p = optionalProject.get();
if (project.getProjectTitle() != null)
p.setProjectTitle(project.getProjectTitle());
if (project.getProjectOverview() != null)
p.setProjectOverview(project.getProjectOverview());
if (project.getStartDate() != null)
p.setStartDate(project.getStartDate());
if (project.getEndDate() != null)
p.setEndDate(project.getEndDate());
if (project.getAssignedTo() != null)
p.setAssignedTo(project.getAssignedTo());
return new ResponseEntity<>(projectRepository.save(p), HttpStatus.OK);
} else
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
That will allow partial update in MongoDB with Spring Boot.
If you are using Spring Data MongoDB, you have two options either use the MongoDB Repository or using the MongoTemplate.
I'm currently using Redis (3.2.100) with Spring data redis (1.8.9) and with Jedis connector.
When i use save() function on an existing entity, Redis delete my entity and re create the entity.
In my case i need to keep this existing entity and only update attributes of the entity. (I have another thread which read the same entity at the same time)
In Spring documentation (https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#redis.repositories.partial-updates), i found the partial update feature. Unfortunately, the example in the documentation use the update() method of RedisTemplate. But this method do not exist.
So did you ever use Spring-data-redis partial update?
There is another method to update entity redis without delete before?
Thanks
To get RedisKeyValueTemplate, you can do:
#Autowired
private RedisKeyValueTemplate redisKVTemplate;
redisKVTemplate.update(entity)
You should use RedisKeyValueTemplate for make partial update.
Well, consider following docs link and also spring data tests (link) actually made 0 contribution to resulting solution.
Consider following entity
#RedisHash(value = "myservice/lastactivity")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class LastActivityCacheEntity implements Serializable {
#Id
#Indexed
#Size(max = 50)
private String user;
private long lastLogin;
private long lastProfileChange;
private long lastOperation;
}
Let's assume that:
we don't want to do complex read-write exercise on every update.
entity = lastActivityCacheRepository.findByUser(userId);
lastActivityCacheRepository.save(LastActivityCacheEntity.builder()
.user(entity.getUser())
.lastLogin(entity.getLastLogin())
.lastProfileChange(entity.getLastProfileChange())
.lastOperation(entity.getLastOperation()).build());
what if there would pop up some 100 rows? then on each update entity got to fetched and saved, quite inefficient, but still would work out.
we don't actually want complex exercises with opsForHash + ObjectMapper + configuring beans approach - it's quite hard to implement and maintain (for example link)
So we're about to use something like:
#Autowired
private final RedisKeyValueTemplate redisTemplate;
void partialUpdate(LastActivityCacheEntity update) {
var partialUpdate = PartialUpdate
.newPartialUpdate(update.getUser(), LastActivityCacheEntity.class);
if (update.getLastLogin() > 0)
partialUpdate.set("lastlastLogin", update.getLastLogin());
if (update.getLastProfileChange() > 0)
partialUpdate.set("lastProfileChange", update.getLastProfileChange());
if (update.getLastOperation() > 0)
partialUpdate.set("lastOperation", update.getLastOperation());
redisTemplate.update(partialUpdate);
}
and the thing is - it doesn't really work for this case.
That is, values getting updated but you can not query new property later on via repository entity lookup: certain lastActivityCacheRepository.findAll() will return unchanged properties.
Here's the solution:
LastActivityCacheRepository.java:
#Repository
public interface LastActivityCacheRepository extends CrudRepository<LastActivityCacheEntity, String>, LastActivityCacheRepositoryCustom {
Optional<LastActivityCacheEntity> findByUser(String user);
}
LastActivityCacheRepositoryCustom.java:
public interface LastActivityCacheRepositoryCustom {
void updateEntry(String userId, String key, long date);
}
LastActivityCacheRepositoryCustomImpl.java
#Repository
public class LastActivityCacheRepositoryCustomImpl implements LastActivityCacheRepositoryCustom {
#Autowired
private final RedisKeyValueTemplate redisKeyValueTemplate;
#Override
public void updateEntry(String userId, String key, long date) {
redisKeyValueTemplate.update(new PartialUpdate<>(userId, LastActivityCacheEntity.class)
.set(key, date));
}
}
And finally working sample:
void partialUpdate(LastActivityCacheEntity update) {
if ((lastActivityCacheRepository.findByUser(update.getUser()).isEmpty())) {
lastActivityCacheRepository.save(LastActivityCacheEntity.builder().user(update.getUser()).build());
}
if (update.getLastLogin() > 0) {
lastActivityCacheRepository.updateEntry(update.getUser(),
"lastlastLogin",
update.getLastLogin());
}
if (update.getLastProfileChange() > 0) {
lastActivityCacheRepository.updateEntry(update.getUser(),
"lastProfileChange",
update.getLastProfileChange());
}
if (update.getLastOperation() > 0) {
lastActivityCacheRepository.updateEntry(update.getUser(),
"lastOperation",
update.getLastOperation());
}
all credits to Chris Richardson and his src
If you don't want to type your field names as strings in the updateEntry method, you can use use the lombok annotation on your entity class #FieldNameConstants. This creates field name constants for you and then you can access your field names like this:
...
if (update.getLastOperation() > 0) {
lastActivityCacheRepository.updateEntry(update.getUser(),
LastActivityCache.Fields.lastOperation, // <- instead of "lastOperation"
update.getLastOperation());
...
This makes refactoring the field names more bug-proof.
I'm currently working on a SpringBoot API to interface with a MongoRepository, but I'm having trouble understanding how the JSON being passed becomes a Document for storage within Mongo. I currently have a simple API that stores a group of users:
#Document
#JsonInclude
public class Group {
#Id
#JsonView(Views.Public.class)
private String id;
#JsonView(Views.Public.class)
private String name;
#JsonView(Views.Public.class)
private Set<GroupMember> groupMembers = new HashSet<>();
}
There are also setter and getter methods for each of the fields, although I don't know how necessary those are either (see questions at the end).
Here is the straightforward component I'm using:
#Component
#Path("/groups")
#Api(value = "/groups", description = "Group REST")
public class Groups {
#Autowired
private GroupService groupService;
#GET
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(value = "Get all Groups", response = Group.class, responseContainer = "List")
#JsonView(Views.Public.class)
public List<Group> getAllGroups() {
return groupService.getAllGroups();
}
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ApiOperation(value = "Create a Group", response = Group.class)
#JsonView(Views.Detailed.class)
public Group submitGroup(Group group) {
return groupService.addGroup(group);
}
}
Finally, I have a Service class:
#Service
public class GroupServiceImpl implements GroupService {
#Autowired
private GroupRepository groupRepository;
#Override
public Group addGroup(Group group) {
group.setId(null);
return groupRepository.save(group);
}
#Override
public List<Group> getAllGroups() {
return groupRepository.findAll();
}
}
The GroupRespository is simply an interface which extends MongoRepository<Group,String>
Now, when I actually make a call to the POST method, with a body containing:
{
"name": "group001",
"groupMembers": []
}
I see that it properly inserts this group with a random Mongo UUID. However, if I try to insert GroupMember objects inside the list, I receive a null pointer exception. From this, I have two questions:
How does SpringBoot (Jackson?) know which fields to deserialize from the JSON being passed? I tested this after deleting the getter and setter methods, and it still works.
How does SpringBoot handle nested objects, such as the Set inside the class? I tested with List instead of Set, and it worked, but I have no idea why. My guess is that for each object that is both declared in my class and listed in my JSON object, SpringBoot is calling a constructor that it magically created behind the scenes, and one doesn't exist for the Set interface.
Suppose I'm adamant on using Set (the same user shouldn't show up twice anyway). What tools can I use to get SpringBoot to work as expected?
It seems to me that a lot of the things that happen in Spring are very behind-the-scenes, which makes it difficult for me to understand why things work when they do. Not knowing why things work makes it difficult to construct things from scratch, which makes it feel as though I'm hacking together a project rather than actually engineering one. So my last question is something like, is there a guide that explains the wiring behind the scenes?
Finally, this is my first time working with Spring... so please excuse me if my questions are entirely off the mark, but I would appreciate any answers nonetheless.
I'm trying to write integration test for Spring Boot application. I have Product and GalleryImage domain model. They are in one-to-many relationship.
public class Product {
...
#OneToMany(mappedBy = "product")
private List<GalleryImage> galleryImages;
}
I have a integration test as below:
#Test
public void testProductAndGalleryImageRelationShip() throws Exception {
Product product = productRepository.findOne(1L);
List<GalleryImage> galleryImages = product.getGalleryImages();
assertEquals(1, galleryImages.size());
}
However, this test gives me a LazyInitializationException. I searched on Google and StackOverFlow, it says that the session is closed after productRepository.findOne(1L), since galleryImages are lazily loaded, so galleryImages.size() gives me this exception.
I have tried to add a #Transactional annotation on the test, but it's still not working.
Hibernate Session has been closed after following line productRepository.findOne(1L).
You can try to do Hibernate.initialize(product.getGalleryImages())
public static void initialize(Object proxy)
throws HibernateException
Force initialization of a proxy or persistent collection.
Note: This only ensures intialization of a proxy object or collection; it is not guaranteed that the elements INSIDE the collection will be initialized/materialized.
To avoid Hibernate.initialize you can create a service.
#Service
#Transactional
public class ProductService {
#Transactional(readOnly = true)
public List<GalleryImage> getImages(final long producId) throws Exception {
Product product = productRepository.findOne(producId);
return product.getGalleryImages();
}
}
If you do use Spring Data JPA in you application then dynamic finder is a good alternative.
I had similar issues in the past and the solution was indeed adding the #Transactional annotation, but contrarily to what is proposed by #Anton M answer I believe that in this case we should annotate the test instead as proposed here.
It works both ways but the annotation should be placed if and where necessary, i.e. add it on the service if you need it there and not just for testing purposes.
For example, keep this as it was:
public class Product {
...
#OneToMany(mappedBy = "product")
private List<GalleryImage> galleryImages;
}
And add the annotation here:
#Test
#Transactional
public void testProductAndGalleryImageRelationShip() throws Exception {
Product product = productRepository.findOne(1L);
List<GalleryImage> galleryImages = product.getGalleryImages();
assertEquals(1, galleryImages.size());
}