There is a use case I am struggling with SDR as below -
THere is User Table and RefSecQuestion tables
User -> ManyTOOne -> RefSecQuestion , RefSecQuestion -> OneToMany -> User
THere is User Table and UserFriends tables
User -> OneToMany UserFriends , UserFriends -> ManyToOne -> User
There is a requirement that when I go /users/{id}/userFriends ,
then firstname , lastname etc from UserProjection should be shown by default
As a result, I enabled excerptProjection in UserRepository and it works fine.
I expect about 100 results here so that is fine if this result is not paginated.
But , now since RefSecQuestion is also related to User , what happens is that when I go
/refSecQuestions -> this page hangs since it is trying to substitute user link with UserProjection. The RefSecQuestion table is skewed with one question for most of users and therefore the page breaks due to loss of pagination.
since i cant choose unidirectionality here as both url are needed i.e
/users/{id}/userFriends
/refSecQuestions/users
THe closest answer I found was to choose unidirectinality which is that I set Rest Export to false for User in RefSEcQuestion
Finally I am able to get my desired results and am posting here for all SDR users.
I wanted to have pagination in this URL - where User -> One To Many -> UserLanguages
/users/{id}/userLanguages
Now Using default SDR configuration, embedded resources don't get paginated and therefore I have to manually expose them and the Workaround is as below which still takes very less lines of code -
#RestController
public class MainController {
#RequestMapping(value = "/users/{id}/userLanguages", method = RequestMethod.GET)
#PreAuthorize("permitAll")
public ModelAndView findUserLanguages(#PathVariable Integer id) {
ModelAndView model = new ModelAndView("forward:/userLanguages/search/findByUserId?userId=" + id);
return model;
}
then, in UserLangRepository
public interface UserLanguageRepository extends BaseRepository<UserLanguage, Integer> {
Page<UserLanguage> findByUserId(#Param("userId") Integer userId, Pageable pageable);
}
Here name findByUserId follows Spring Data Query Derivation rule where there is user column in UserLanguage and id column in User.
Then following URL is paginated and have other options as well like sort, size etc..
http://localhost:8585/MYAPP/users/3/userLanguages
There is an issue however that next and prev links point to forwarded link..
Related
I am working with Spring Data 2.0.6.RELEASE.
I am working about pagination for performance and presentation purposes.
Here about performance I am talking about that if we have a lot of records is better show them through pages
I have the following and works fine:
interface PersonaDataJpaCrudRepository extends PagingAndSortingRepository<Persona, String> {
}
The #Controller works fine with:
#GetMapping(produces=MediaType.TEXT_HTML_VALUE)
public String findAll(Pageable pageable, Model model){
Through Thymeleaf I am able to apply pagination. Therefore until here the goal has been accomplished.
Note: The Persona class is annotated with JPA (#Entity, Id, etc)
Now I am concerned about the following: even when pagination works in Spring Data about the amount the records, what about of the content of each record?.
I mean: let's assume that Persona class contains 20 fields (consider any entity you want for your app), thus for a view based in html where a report only uses 4 fields (id, firstname, lastname, date), thus we have 16 unnecessary fields for each entity in memory
I have tried the following:
interface PersonaDataJpaCrudRepository extends PagingAndSortingRepository<Persona, String> {
#Query("SELECT p.id, id.nombre, id.apellido, id.fecha FROM Persona p")
#Override
Page<Persona> findAll(Pageable pageable);
}
If I do a simple print in the #Controller it fails about the following:
java.lang.ClassCastException:
[Ljava.lang.Object; cannot be cast to com.manuel.jordan.domain.Persona
If I avoid that the view fails with:
Caused by:
org.springframework.expression.spel.SpelEvaluationException:
EL1008E:
Property or field 'id' cannot be found on object of type
'java.lang.Object[]' - maybe not public or not valid?
I have read many posts in SO such as:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to
I understand the answer and I am agree about the Object[] return type because I am working with specific set of fields.
Is mandatory work with the complete set of fields for each entity? Should I simply accept the cost of memory about the 16 fields in this case that never are used? It for each record retrieved?
Is there a solution to work around with a specific set of fields or Object[] with the current API of Spring Data?
Have a look at Spring data Projections. For example, interface-based projections may be used to expose certain attributes through specific getter methods.
Interface:
interface PersonaSubset {
long getId();
String getNombre();
String getApellido();
String getFecha();
}
Repository method:
Page<PersonaSubset> findAll(Pageable pageable);
If you only want to read a specific set of columns you don't need to fetch the whole entity. Create a class containing requested columns - for example:
public class PersonBasicData {
private String firstName;
private String lastName;
public PersonBasicData(String firstName, String lastName) {
this.firstName = fistName;
this.lastName = lastName;
}
// getters and setters if needed
}
Then you can specify query using #Query annotation on repository method using constructor expression like this:
#Query("SELECT NEW some.package.PersonBasicData(p.firstName, p.lastName) FROM Person AS p")
You could also use Criteria API to get it done programatically:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<PersonBasicData> query = cb.createQuery(PersonBasicData.class);
Root<Person> person = query.from(Person.class);
query.multiselect(person.get("firstName"), person.get("lastName"));
List<PersonBasicData> results = entityManager.createQuery(query).getResultList();
Be aware that instance of PersonBasicData being created just for read purposes - you won't be able to make changes to it and persist those back in your database as the class is not marked as entity and thus your JPA provider will not work with it.
Is it possible to use Spring Data Rest and Spring Security to return current user related entities, using the findAll() method without specifying this user in the GET query parameter?
My only solution is to pass user as a parameter, but maybe it's another option to get him from SpringSecurityContext
public interface InvoiceRepository extends CrudRepository<Invoice, Long> {
#RestResource
#PreAuthorize("hasRole('ROLE_ADMIN') or user?.username == authentication.name")
List<Invoice> findAllByUser(#Param("user") User user);
You can use SpEL EvaluationContext extension that makes security properties and expressions available in SpEL expressions in the #Query annotations. This allows you to get only those business objects that relate to the current user:
interface SecureBusinessObjectRepository extends Repository<BusinessObject, Long> {
#Query("select o from BusinessObject o where o.owner.emailAddress like ?#{hasRole('ROLE_ADMIN') ? '%' : principal.emailAddress}")
List<BusinessObject> findBusinessObjectsForCurrentUser();
}
More details are here.
I'm trying to understand how to properly implement a microservice pattern with REST endpoints.
I understand the very basics of it. The internet is full of that. Typical example:
class User
{
public User GetUser(int id) { ... }
public User GetUsers() { ... }
public User PutUser(User user) { ... }
public User PostUser(User user) { ... }
}
So if I want one User then I'd do a GET request to GetUser(100), and it would return a User object as JSON.
But suppose I have a page where I want to list the firstname and lastname of all users. Then I could do a GET request to GetUsers(). But if the User table has 100 columns in the Database and I only need to display two columns (firstname, lastname), then it's overkill to get the other 98 columns along with it.
Maybe on another page I need to display 40 of the 100 columns. And on some other page 20 of the 100 columns.
So that means I need 3 extra end points that all return all users. But each end point should return different data.
How would I name those new endpoints?
Do I have to call those 3 endpoints explicitly by name? (Ex: GetUsersNames() GetUsersAge() etc?)
PS. This is probably a poor example, but I hope you understand what I'm getting at. I don't know what to do / how to name the endpoints when I go beyond the default GET/PUT/POST/DELETE methods.
What you can do is to ask for the particular fields explicitly, so:
GET /api/users/?fields=firstName,lastName
This is much better than introducing new endpoints.
You can define custom views by using Attribute Routing. The methods you have in your controller default to the route pattern that you define in your configuration.
You can however explicitely define additional routes. You need to include this in your Configuration:
// this enables route attributes (route annotations at the actions in the controller)
config.MapHttpAttributeRoutes();
Then you can define additional routes in your controller and add the required filter logic:
[Route("api/users/range/{rangeFrom}/{rangeTo}")]
[HttpGet]
public IHttpActionResult GetUsers(int from, int to)
{
return Ok(Users.Skip(from).Take(to-from));
}
[Route("api/users/namesonly")]
[HttpGet]
public IHttpActionResult GetUsers()
{
return Ok(Users.Select(u => new { firstName = u.FirstName, lastName = u.LastName }));
}
Reference: Attribute Routing in ASP.NET Web API 2
EDIT:
Yes, not all options should be resolved in separate endpoints, but it's an option at a certain point. What others do: Use URI parameters to limit returned data count or set flags, and have them available through the method's signature e.g.:
[Route("api/users/")]
[HttpGet]
public IHttpActionResult GetUsers([FromUri]int? from, [FromUri]int? count)
{
return Ok(Users.Skip(from ?? 0).Take(count ?? Users.Count));
}
Or to have shorter user objects returned:
[Route("api/users/")]
[HttpGet]
public IHttpActionResult GetUsers([FromUri]bool? shortVersion)
{
if (shortVersion.HasValue && shortVersion.Value)
return Ok(Users.Select(u => new { firstName = u.FirstName, lastName = u.LastName }));
else
return Ok(Users);
}
I have a GridView which needs to page and sort data which comes from a collection of Customer objects.
Unfortunately my customer information is stored separately...the customer information is stored as a Customer ID in my database, and the Customer Name in a separate DLL.
I retrieve the ID from the database using Entity Framework, and the name from the external DLL through a partial class.
I am getting the ID from my database as follows:
public class DAL
{
public IEnumberable<Customer> GetCustomers()
{
Entities entities = new Entities();
var customers = (from c in entities.Customers
select c);
//CustomerID is a field in the Customer table
return customers;
}
}
I have then created a partial class, which retrieves the data from the DLL:
public partial class Customer
{
private string name;
public string Name
{
if (name==null)
{
DLLManager manager = new DLLManager();
name= manager.GetName(CustomerID);
}
return name;
}
}
In my business layer I can then call something like:
public class BLL
{
public List<Customer> GetCustomers()
{
DAL customersDAL = new DAL();
var customers = customersDAL.GetCustomers();
return customers.ToList();
}
}
...and this gives me a collection of Customers with ID and Name.
My problem is that I wish to page and sort by Customer Name, which as we have seen, is populated from a DLL. This means I cannot page and sort in the database, which is my preferred solution. I am therefore assuming I am going to have to call of the database records into memory, and perform paging and sorting at this level.
My question is - what is the best way to page and sort an in-memory collection. Can I do this with my List in the BLL above? I assume the List would then need to be stored in Session.
I am interested in people's thoughts on the best way to page and sort a field that does not come from the database in an Entity Framework scenario.
Very grateful for any help!
Mart
p.s. This question is a development of this post here:
GridView sorting and paging Entity Framework with calculated field
The only difference here is that I am now using a partial class, and hopefully this post is a little clearer.
Yes, you can page and sort within you list in the BLL. As long as its fast enough I wouldn't care to much about caching something in the session. An other way would be to extend your database with the data from you DLL.
I posted this question slightly differently on a different forum, and got the following solution.
Basically I return the data as an IQueryable from the DAL which has already been forced to execute using ToList(). This means that I am running my sorting and paging against an object which consists of data from the DB and DLL. This also allows Scott's dynamic sorting to take place.
The BLL then performs OrderBy(), Skip() and Take() on the returned IQueryable and then returns this as a List to my GridView.
It works fine, but I am slightly bemused that we are perfoming IQueryable to List to IQueryable to List again.
1) Get the results from the database as an IQueryable:
public class DAL
{
public IQueryable<Customer> GetCustomers()
{
Entities entities = new Entities();
var customers = (from c in entities.Customers
select c);
//CustomerID is a field in the Customer table
return customers.ToList().AsQueryable();
}
}
2) Pull the results into my business layer:
public class BLL
{
public List<Customer> GetCustomers(intint startRowIndex, int maximumRows, string sortParameter)
{
DAL customersDAL = new DAL();
return customersDAL.GetCustomers().OrderBy(sortParameter).Skip(startRowIndex).Take(maximumRows).ToList();
}
}
Here is the link to the other thread.
http://forums.asp.net/p/1976270/5655727.aspx?Paging+and+sorting+Entity+Framework+on+a+field+from+Partial+Class
Hope this helps others!
I have a little problem with RequestFactory regarding persistence of children collections in the shape of Set . I am using gwt 2.5 with requestfactory, and Hibernate4/Spring3 at the backend. I am using the open-session-in-view filter by Spring so that collections can be persisted after findByID in the save method of my DAO. My problem is everything seems to work ok when children collections are based on List , but when they are based on Set , not all of the items from the client reach the server aparently.
My code looks like this:
-The root entity IndicationTemplate:
#Entity
#Table (name = "vdasIndicationTemplate")
#org.hibernate.annotations.Table ( appliesTo = "vdasIndicationTemplate", indexes =
{#Index (name = "xieIndicationTemplateCreateUser", columnNames= {"createUserID"}),
#Index (name = "xieIndicationTemplateModifyUser", columnNames= {"modifyUserID"})})
public class IndicationTemplate extends AbstractEditable <Integer> implements IEntity <Integer>, IDateable, IDescriptable {
//...
private Set <ProposalTemplate> proposalTemplates = null;
//...
#OneToMany (fetch = FetchType.LAZY, mappedBy = "indicationTemplate"
, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.DETACH})
public Set <ProposalTemplate> getProposalTemplates () {
return proposalTemplates;
}
public void setProposalTemplates (Set <ProposalTemplate> proposalTemplates) {
this.proposalTemplates = proposalTemplates;
}
//...
}
-The child entity ProposalTemplate of course has the opposite ManyToOne mapping and has 3 sub-collections as well of the same sort with 3 different entities.
-Client-side proxy for root entity:
#ProxyFor (value = IndicationTemplate.class, locator = PersistenceEntityLocator.class)
public interface IIndicationTemplateProxy extends IEntityProxy, IDeletableProxy, IDescriptableProxy {
//....
Set <IProposalTemplateProxy> getProposalTemplates ();
void setProposalTemplates (Set <IProposalTemplateProxy> proposalTemplateProxy);
}
-On the client, i render the attributes of root entity and also the list of children entity. Then the user can update them, and the changes are stored back into the collection like this:
Set <IProposalTemplateProxy> newList = getElementsFromUiSomehow (); //these elements can be new or just the old ones with some changes
indicationTemplate.getProposalTemplates ().clear ();
indicationTemplate.getProposalTemplates ().addAll (newList);
-And then at some point:
requestContext.saveIndicationTemplate ((IIndicationTemplateProxy) entityProxy)
.fire (new Receiver <IIndicationTemplateProxy> ()
-The RequestContext looks something like:
#Service (value = TemplateService.class, locator = SpringServiceLocator.class)
public interface ITemplateRequestContext extends RequestContext {
/** saves (creates or updates) one given indication template */
Request <IIndicationTemplateProxy> saveIndicationTemplate (IIndicationTemplateProxy indicationTemplate);
//....
}
The problem is only 1 child entity is added per request to the collection server-side. For example, indicationTemplate has 2 proposalTemplates, and i add 4 more, then on the server-side saveIndicationTemplate the entity contains only 3 instead of 6. If happens no matter how many entities i have previously and how many i add, i only get 1 more than before on the server. I did check the proxy object right before firing the requestContext method and it is fully loaded, with all of its children. And finally the weirdest thing is, if i replace Set per List (and all subsequent changes), everything works sweet!
May there be any problem why RF fails to transfer all the changes to the server when using Sets instead of Lists?? Btw, i do prefer Sets in this case, so that is why i am asking.
Anyone?
Thanks for helping!
I assume you are hitting this error. It is a known gwt bug which is still unfixed.
https://code.google.com/p/google-web-toolkit/issues/detail?id=6354&q=set&colspec=ID%20Type%20Status%20Owner%20Milestone%20Summary%20Stars
try to use list instead of set and it should be fine.