public List<Post> getPosts(String city) {
// if City parameter is presented(When searched by an user)
if (!Strings.isNullOrEmpty(city)) {
return postRepository.findAllByCityOrderByIdDesc(city).stream().map(obj -> {
obj.getUser().setPassword("");
return obj;
}).collect((Collectors.toList()));
} else {
return postRepository.findAllByOrderByIdDesc().stream().map(obj -> {
obj.getUser().setPassword("");
return obj;
}).collect((Collectors.toList()));
}
}
I tried to change object values with the stream map function after fetching data from the DB, and the values in the DB changed too.
are they two connected?
The fact that you're using a stream to operate on the values in the list is entirely irrelevant:
JPA is a "magic" technology that specifically provides the feature of automatically persisting changes that you make to JPA entity objects.
The stream() just takes an existing collection of objects and operates on them.
You are modifying the objects in map, which is not recommended in general. You could detach the users from the EntityManager, but...
you should be mapping these to DTOs rather than presenting the entity objects to your top-level API layer, and
when you have chunks like this that need to be isolated, you should consider separating your database model so that the "public profile" part of the user and the authentication part aren't grouped together.
Related
I'm trying to implement a method for updating a database record. So far, I created this one:
public Optional<User> update(final Integer id,final UpdateUserDto dto) {
userRepository.findById(id).ifPresent((user -> {
user.setShop((dto.getShopId() == null) ? null : shopRepository.findById(dto.getShopId())
.orElseThrow(ShopNotFoundException::new));
user.setUsername(dto.getUsername());
user.setPassword(passwordEncoder.encode(dto.getPassword()));
user.setRoles(Arrays.asList(
roleRepository.findByRoleName(dto.getRole()).orElseThrow(RoleNotFoundException::new)
));
}));
return userRepository.findById(id);
}
But now I added two more fields to my user entity (activated, productAllowed) and I must enhance my update method to make them updatable. I can do that, but I have other entities also and if I change them it will be a lot of maybe boilerplate code.
Is there any kind of best practice to do this in a better way, or I just need to keep setting all the fields manually?
I was also thinking about reflection, but in that case I have a few fields that cannot be copied exactly from the DTO to the entity (e.g. the shop field, which is queried from database, or role field).
And I also don't think that another query for returning the new object is effective, but although I set the properties in a service layer, the original findById()'s returned user is wrapped inside an Optional, so I don't think it will be updated.
Thank you in advance.
I have the following simple code:
#Test
public void saveExpense() {
// Create dummy Expense object i.e. { "description": "Short Description", "date": etc }
Expense expenseToSave = ExpenseHelper.createExpense("Short Description", new Date(), user);
Expense savedExpense = expenseService.save(expenseToSave);
// What is strange, is that here, both expenseToSave and savedExpense have id set to 1 for example; after save the expense should have an id;
Expense expected = ExpenseHelper.createExpense("Short Description", new Date(), user);
// Check if expected object is equal to the saved one
Assert.assertTrue(expected.equals(expenseService.findByDescription("Short Description")));
}
Normally I would expect that expenseToSave to be without id and savedExpense with id, but both have id after save. Why?
That made another variable to be necessary and complicate the test.
Thanks.
That's just how the Hibernate Session.save() method is specified. From the documentation:
Persist the given transient instance, first assigning a generated
identifier. (Or using the current value of the identifier property if
the assigned generator is used.) This operation cascades to associated
instances if the association is mapped with cascade="save-update".
IDs are the mechanism how Hibernate differentiates between persisted and transient objects, and how it identifies specific objects. Therefore, the ID is set early in the persistence step, as for example cyclic references in an object tree are resolved via IDs while persisting.
What differentiates the returned object vs. the original object is that the returned object is attached to the Hibernate session. For example, with active cascading, contained entities (e.g. in a one-to-many collection) are now persistent instances as well in the returned object.
Please be aware that
void EntityManager#persist(java.lang.Object entity)
(http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#persist%28java.lang.Object%29)
Persists the given object by changing the object passed in and does not return a persisted copy - I suspect your ExpenseHelper to return the original object additionally so that you receive the same object via return as you already have by passing it in.
This follows a common anti-pattern for a kind of unified behaviour of DAO to be something like
public T create(T entity) {
this.entityManager.persist(entity);
return entity;
}
to get a kind of synchronicity with saving something
public T save(T entity) {
return this.entityManager.merge(entity);
}
Where
<T> T EntityManager#merge(T entity)
does indeed merge and pass you the merged entity.
It can depend on Hibernate mapping of the Expense entity, or implementation of ExpenseHelper class.
Also, take a look on Expense.equals() implementation.
Based on this statement:
Expense savedExpense = expenseService.save(expenseToSave);
the value of the savedExpense object will depend on what your are doing in the save method. Usually save methods don't return an object. You already have a reference to the object that you just saved (expenseToSave) available to you. And you are trying to assert that your expected object equals the object that was saved, which is fine. So I am not sure what the purpose of returning an object in expenseService.save(expenseToSave)
Also, note that the id of the object expenseToSave would have been populated by your ORM (Hibernate, I assume) based on your configuration, when you save it. There is no need to return this object or another object in the save method.
I know that when using Wicket with JPA frameworks it is not advisable to serialize entities that have already been persisted to the database (because of problems with lazy fields and to save space). In such cases we are supposed to use LoadableDetachableModel. But what about the following use-case?
Suppose we want to create a new entity (say, a Contract) which will consist, among other things, of persisted entities (say, a Client which is selected from a list of clients stored in the DB). The entity under creation is a model object of some Wicket component (say, a Wizard). In the end (when we finish our wizard) we save the new entity to the DB. So my question is: what is the best generic solution to the serialization problem of such model objects? We can't use LDM because the entity is not in the DB yet but we don't want our inner entities (like Client) to be serialized wholly, too.
My idea was to implement a custom wicket serializer that checks if the object is an entity and if it is persisted. If so, store only its id, otherwise use the default serialization. Similarly, when deserializing use the stored id and get the entity from the DB or deserialize using the default mechanism. Not sure, though, how to do that in a generic way. My next thought was that if we can do it, then we do not need any LDM anymore, we can just store all our entities in simple org.apache.wicket.model.Model models and our serialization logic will take care of them, right?
Here's some code:
#Entity
Client {
String clientName;
#ManyToOne(fetch = FetchType.LAZY)
ClientGroup group;
}
#Entity
Contract {
Date date;
#ManyToOne(fetch = FetchType.LAZY)
Client client;
}
ContractWizard extends Wizard {
ContractWizard(String markupId, IModel<Contract> model) {
super(markupId);
setDefaultModel(model);
}
}
Contract contract = DAO.createEntity(Contract.class);
ContractWizard wizard = new ContractWizard("wizard", ?);
How to pass the contract? If we just say Model.of(contract) the whole contract will be serialized along with inner client (and it can be big), moreover if we access contract.client.group after deserialization we can bump into the problem: https://en.wikibooks.org/wiki/Java_Persistence/Relationships#Serialization.2C_and_Detaching
So I wonder how people go about solving such issues, I'm sure it's a fairly common problem.
I guess there are 2 approaches to your problem:
a.) Only save the stuff the user actually sees in Models. In your example that might be "contractStartDate", "contractEndDate", List of clientIds. That's the main approach if you don't want your DatabaseObjects in your view.
b.) Write your own LoadableDetachableModel and make sure you only serialize transient objects. For example like: (assuming that any negative id is not saved to the database)
public class MyLoadableDetachableModel extends LoadableDetachableModel {
private Object myObject;
private Integer id;
public MyLoadableDetachableModel(Object myObject) {
this.myObject = myObject;
this.id = myObject.getId();
}
#Override
protected Object load() {
if (id < 0) {
return myObject;
}
return myObjectDao.getMyObjectById(id);
}
#Override
protected void onDetach() {
super.onDetach();
id = myObject.getId();
if (id >= 0) {
myObject = null;
}
}
}
The downfall of this is that you'll have to make your DatabaseObjects Serializable which is not really ideal and can lead to all kind of problems. You would also need to decouple the references to other entities from the transient object by using a ListModel.
Having worked with both approaches I personally prefer the first. From my expierence the whole injecting dao objects into wicket can lead to disaster. :) I would only use this in view-only projects that aren't too big.
Most projects I know of just accept serializing referenced entities (e.g. your Clients) along with the edited entity (Contract).
Using conversations (keeping a Hibernate/JPA session open over several requests) is a nice alternative for applications with complex entity relations:
The Hibernate session and its entities is kept separate from the page and is never serialized. The component just keeps an identifier to fetch its conversation.
Hi there i need help I'm new to caching data. I'm using ehcache in spring application using xml configuration and I want to use different keys on different method to find same record. Suppose, one method is annotated like this:
#Cacheable(value="getCustomerByAreaId",key="T(java.lang.String).valueOf(#areaid)")
public List<Customer> getCustomerByAreaId(String areaid) {
//code
return customerList;
}
it will return all the customers having same area id. This list will be stored in the cache as each customer have unique customer id. Can I use some mechanism to fetch single customer record from cache= getCustomerByAreaId based on customer id.
#Cacheable(value="getCustomerByAreaId",key="T(java.lang.String).valueOf(#customerId)")
public Customer getCustomerById(long customerId) {
// code
return customer;
}
I know if I make key like this it will enter a new record in the cache (getCustomerByAreaId) with the new key.
Instead I want to fetch record from list that is already being cached.
If it is possible can I do this using xml or java.
I'm using ehcache version 2.5.
This is not possible simply by using Ehcache APIs or Spring's caching abstraction.
If you want to achieve this, you will have to program your own logic to cache the list but also its elements individually.
As part of my program, I'm using relational tables which hold information such as - user role, job category etc. Each table may have slightly differing fields - for example:
User Role Table has the following fields:
id (auto-generated)
role (eg Planner, Admin etc)
role_description (description of above role)
enabled (toggle this role on/off)
Job Category Table:
id (auto-generated)
category (eg Service, Maintenance etc)
category_description (description of above)
category_group (categories are grouped into management areas)
...
enabled (toggle category on/off)
The lists can be changed by the end user so I need to provide an admin section to enable new roles/categories to be added.
I had thought of creating a routine where I pass the entity class of the role/category etc and have it generate an array which can be used to populate the admin section but have only been able to do this for the 1st two columns - eg id/role or id/category.
With the fields differing between each entity, is there a way that I can do this? Or will I have to create a method in each of the entities - such as getRoleList and getCategoryList etc?
Thanks.
After a bit of experimenting, I've decided to implement this in the following way.
I've added methods to my database helper class that will read the list and populate an array. I'll have to create a separate method for each entity but I've decided this would be necessary due to the differences between the classes.
I'm not 100% sure that this is the most efficient way of accomplishing this but it does what I need (for now).
One of the methods:
public static UserRole[] getUserRoleList(String order, Boolean reverseOrder) throws SQLException {
Session session = openSession();
List<UserRole> list;
if (!reverseOrder) {
// obtain list and sort by provided field in ascending order
list = session.createCriteria(UserRole.class).addOrder(Order.asc(order)).list();
} else {
// sort descending
list = session.createCriteria(UserRole.class).addOrder(Order.desc(order)).list();
}
// return UserRole[]
return list.toArray((UserRole[]) Array.newInstance(UserRole.class, list.size()));
}
The rest of the methods will be pretty much identical (substituting the entity/class names). The only difference would be adding another argument for some entities (enabled Boolean, so I can return only items in the list which are enabled).
Edit:
Since posting the above, I changed my mind and moved to a generic method to obtain lists, passing in the entity class as below:
public static List getList(Class entity, String order, Boolean reverseOrder, Boolean enabled) {
// stripped for brevity...
list = session.createCriteria(entity)
.add(Restrictions.eq("enabled", true))
.addOrder(Order.asc(order)).list();
// stripped more...
return list;
}
Casting when calling the method:
List<User> userList = DatabaseHelper.getList(User.class);