How can I write a repository method for this situation? - java

I know this isn't the best way to define a car class, but I got curious and I ended up writing this:
#Entity
#Table(name = "CAR")
public class Car {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String manufacturer;
private String model;
#ElementCollection
#CollectionTable(
name="ACCESSORY",
joinColumns=#JoinColumn(name="OWNER_ID")
)
#Column(name="ACCESSORIES")
private List<String> accessories;
//getters and setters
}
and its repository
#Repository
public interface CarRepository extends JpaRepository<Car, Long> {
}
Now, considering that I want to create a repository method that finds all the cars that meet a given list of accessories, how can I do it? I was thinking about a method like findById() or something.
By the way, feel free if you want to answer in a way that "Accessories" is an entity.
Disclaimer: Yes, I tried the method List<Car> findByAccessoriesIn(List<String> as); but it brings any car that has at least one of the elements inside the list. I want all the cars that have all items inside the list.

“finds all the cars that meet a given list of accessories“
If i understood correctly then -
findByAccessoriesIn(List yourStrList) should work in this case.
A similar thread is here:
Spring CrudRepository findByInventoryIds(List<Long> inventoryIdList) - equivalent to IN clause
For List in jpa we use “in” where “i” is in caps.

Related

How should a DTO look like compared to an Entity

Let's assume an application where there are leagues and teams inside of leagues, and teams can be in multiple leagues aswell. So we do have a many to many relationship.
League Entity
#Data
#Entity
public class League {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String countryCode;
private SportType sportType;
#ManyToMany(mappedBy = "leagues")
private List<Team> teams;
}
Team Entity
#Data
#Entity
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String logoUrl;
private SportType sportType;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "team_league",
joinColumns = #JoinColumn(name = "team_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "league_id", referencedColumnName = "id"))
private List<League> leagues;
}
I am now on the point where i need for example to create a new team, and upon creation, it needs a league to put in, which has to already exist. That means i need an endpoint which takes a list of leagueIds instead of a List<League>. So i assume i need to build a DTO. But how should the DTO look like and how would i implement the method that maps the DTO to an entity and saves it to the database.
My idea of the TeamDTO
#Data
public class TeamDTO {
private Long id;
private String name;
private String logoUrl;
private SportType sportType;
private List<Integer> leagueIds;
}
So instead of a List<League> i do have a List<Integer> leagueIds so that the endpoint can accept proper JSON. Is that correct?
Now i want to create the team in the database, IF the leagues of List<Integer> leagueIds are present in the database. So my question now is, when do i map to the entity.
My idea of the implementation of the service
public class TeamServiceImpl implements TeamService {
#Autowired
private LeagueRepository leagueRepository;
#Autowired
private TeamRepository teamRepository;
#Override
public Team createTeam(TeamDTO teamDTO) {
List<Long> ids =
teamDTO.getLeagueIds().stream().filter(leagueId ->
leagueRepository.findById(leagueId).isPresent()).
collect(Collectors.toList();
if (!ids.isEmpty()) {
Team team = new Team();
team.setName(teamDTO.getName());
team.setLogoUrl(teamDTO.getLogoUrl());
team.setSportType(teamDTO.getSportType());
// do i actually need the League entities to set this?
team.setLeagues(...);
return team;
}
return null;
}
}
Most important question is: Is this the correct way?
Should i use a mapper for DTO to entity and vice versa?
Should i implement a mapper myself (i mean it only maps a few
fields)?
And on what place i would use the mapper, if i would implement one?
I don't know why you only want to save the team if it has leagues assigned that exist. It just sounds wrong to me i.e. some kind of bug is in your app if the league for an id does not exist. You should set a list of league references and rely on the FK-constraint to error if a wrong league id is used i.e. use something like this:
List<League> leagues =
teamDTO.getLeagueIds().stream().map(leagueId ->
leagueRepository.getOne(leagueId)).
collect(Collectors.toList());
team.setLeagues(leagues);
The DTO approach is fine and as long as it stays this simple, I guess using this custom implementation is good enough. If you have more complex requirements and want to make use of more efficient processing I would recommend you look into Blaze-Persistence Entity-Views which was made for exactly this purpose, efficient mapping between JPA entities and DTOs.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Team.class)
#UpdatableEntityView
public interface TeamDTO {
#IdMapping
Long getId();
String getName();
void setName(String name);
String getLogoUrl();
void setLogoUrl(String logoUrl);
SportType getSportType();
void setSportType(SportType sportType);
#UpdatableMapping
#JsonIgnore
List<LeagueDto> getLeagues();
default List<Long> getLeagueIds() {
return getLeagues().stream().map(LeagueDto::getId).collect(toList());
}
default void setLeagueIds(List<Long> ids) {
getLeagues().clear();
ids.stream().map(id -> evm().getReference(LeagueDto.class, id)).forEach(getLeagues()::add);
}
// This is a special context providing method
EntityViewManager evm();
#EntityView(League.class)
interface LeagueDto {
#IdMapping
Long getId();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
TeamDTO a = entityViewManager.find(entityManager, TeamDTO.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<TeamDTO> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
The saving part will then be as simple as this:
public class TeamServiceImpl implements TeamService {
#Autowired
private TeamRepository teamRepository;
#Override
public Team createTeam(TeamDTO teamDTO) {
teamRepository.save(teamDTO);
return teamRepository.getOne(teamDTO.getId());
}
}
Due to the change-tracking implementation of Entity-Views, at any point in time it is clear what is dirty and will by default only flush these changes and avoid unnecessary select statements during flushing.

Crnk JsonApiRelation, OneToMany and filtering implementation

I use crnk (JSON-API) in java project and I have 3 questions regarding its usage with spring boot and jpa - haven't found exact implementation details in documentation.
For example, I have 2 entities and respective tables:
#Entity
#JsonApiResource(type = "employee")
public class Employee {
#Id
#JsonApiId
private int id;
private String name;
#ManyToOne
#JoinColumn(name = "typeId")
private EmployeeType employeeType; //stored in table as typeId
}
#Entity
#JsonApiResource(type = "type")
public class EmployeeType {
#Id
#JsonApiId
private int id;
private String typeName;
private int salary;
}
How should JsonApiRelation be introduced in order to be able to call "/employee/1" and "/employee/1/type" urls?
For example there is one more entity.
#Entity
#JsonApiResource(type = "project")
public class Project {
#Id
#JsonApiId
private int id;
private String supervisorName;
private String projectName;
}
First, I'd like to have List of Projects for each Employee, where he is a supervisor, joint by name and have it listed as attribute in Json.
Tried implementing it with #OneToMany and #JoinColumn annotations but got StackOverflowException. How could this be implemented. And second, how could this be implemented with Relation? Like "/employee/1/projects" url.
How should I implement custom filtering of results for findAll method? For example, I have a List of all Employees, but I'd like to exclude some of them from the response. Which class/method should be introduced for this behaviour?
#JsonApiRelation annotation should not be necessary. Crnk will detect the #ManyToOne annotation and map it accordingly.
in case of crnk-jpa it is sufficient to specify all relationships in JPA. Matching JSON API relationships. So your approach seems good. What was the StackoverflowException stacktrace? (next to the examples, there are also many example entities in crnk-jpa)
I would make use of a decorator. See http://www.crnk.io/documentation/#_request_filtering. RepositoryDecoratorFactory allows to place a custom repository between the caller and crnk-jpa (or any other kind of repository). There you can do any kind of modification perform (maybe) calling the "real" repository. => Will add an example for this
feel free also make open up tickets in crnk for any documentation/example clarifications.

Why Nested Objects is retrieved from JPA? [duplicate]

I'm using SpringBoot and JPA to build a REST interface.
Now, I have a strange JSON returned for the list of products fetched from the database. Let's say that I have:
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "categoryId", nullable = false, updatable = false)
private Category category;
...
}
#Entity
public class Category implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "category", cascade = CascadeType.DETACH)
#OrderBy("name ASC")
private List<Product> products = Collections.emptyList();
...
}
The JPA repository for the Product is defined as:
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findAll();
}
In my controller I have:
#Autowired
private ProductRepository productRepo;
#RequestMapping("/all-products", method = RequestMethod.GET)
public Map<String,Object> home() {
Map<String,Object> model = new HashMap<String,Object>();
model.put("products", productRepo.findAll());
return model;
}
What is driving my crazy, is that if I try to call this service as follows:
$ curl localhost:8080/all-products
I get a recursive output due to the relationship between tables product and category, e.g.:
{"products":[{"id":1,"name":"Product1","category":
{"id":1,"name":"Cat1","products":[{"id":6,"name":"Product6","category":
{"id":1,"name":"Cat1","products":[{"id":6,"name":"Product6","category":
{"id":1,...
What am I doing wrong?
You're not doing anything wrong (at least at the code level it's rather conceptual) - json serializer just goes like this:
Product - serialize it, but wait - there is a category field, so serializer must serialize the category field
Category - serialize it, but wait - there is a products field, so serializer must serialize each of the product in the list
Product - because your collection contains the product & product contains category it goes in a endless loop untill a timeout.
You must use a view or just skip it.
Use #JsonView
Use a view as a POJO
Return new ProductView that has all fields of product and a reference (category) to new CategoryView (you can end at this point) that has collection of (products) new ProductViewWithoutReferences, and so on
Use #JsonIgnore on a collection of products
And as a side note - if it's a #RestController and you're invoking "all-products" then it's a bit unusual to return something else than a list. Wrapping the response in a map is redundant. Many rest clients expect a list when they invoke list() method.
I know it's a bit late, but adding it here in case anybody faces the same problem.
Here is another relevant answer I could find which discuss about similar topic
https://stackoverflow.com/a/3359884/6785908
quoting it here
Jackson 1.6 has annotation-based support for handling such
parent/child linkage, see
http://wiki.fasterxml.com/JacksonFeatureBiDirReferences.
You can of course already exclude serialization of parent link already
using most JSON processing packages (jackson, gson and flex-json at
least support it), but the real trick is in how to deserialize it back
(re-create parent link), not just handle serialization side. Although
sounds like for now just exclusion might work for you.
EDIT (April 2012): Jackson 2.0 now supports true identity
references, so you can solve it this way also.
Adding #JsonIgnore worked for me
#OneToMany(mappedBy = "policy")
#JsonIgnore
private List<Payment> payments;
#JeanValjean your are the best

Stackoverflow-Exception while loading RelationshipEntity in OGM

I have the following classes:
#NodeEntity
public class Item{
//...
}
#RelationshipEntity(type = "HAS")
public class HasRelation{
//...
#StartNode
private User user;
#EndNode
private Item item;
}
#NodeEntity
public class User{
//...
#Relationship(type="HAS")
private Set<HasRelation> has;
}
So now I have a User Sven with ID 1 having an Item Hammer in the Database and want to load it.
When I call the OGM session.load(User.class, 1) I always get an Stackoverflow-Exception, because the User hold a Relationship, holding the User, holding a relationship, and so on.
This feels like the wrong way to use OGM for me and I don't want to restrict the Depth by which I load to 0.
However the OGM specification tells me, that there is no other way, since the RelationshipEntity needs a Start- and EndNode and has to be referenced in one of those.
So I don't see a way to prevent this Exception other than resticting the Loading-Depth to 0.
Is there a better way?
You are exposing the data as JSON. The converter also needs to traverse the
'object tree' and this causes the stackoverflow.
The solution is simple: You are defining an outgoing relationship in the User class so this object does not need to be visited again when the jackson lib hits the relationship.
#RelationshipEntity(type = "LIKES")
public class LikedBook {
#Id
#GeneratedValue
private Long id;
private String how;
#StartNode
#JsonIgnore // <- "do not go back"
private User user;
#EndNode
private Book book;

Update or saveorUpdate in CRUDRepository

I know there's already a similar question answered previously, but my problem is implementing save with update while there are 3 methods in the interface.
I'm currently using the following methods in my project and don't know how to make saveOrUpdate in this.
The following are my classes:
public interface CompanyRepository extends CrudRepository<Company,Long>{
Company findByCompanyName (String companyName);
List<Company> findAll();
Company findById(Long id);
}
The following is part of my Company Class
#Entity
public class Company extends BaseEntity{
#NotNull
#Size(min = 2, max = 16)
private String companyName;
#Length(max = 60)
private String desc;
//TODO max: add LOGO class later for pic saving
#OneToMany
private List<MobileModel> mobileModels;
public Company()
{
super();
mobileModels = new ArrayList<>();
}
//Getters n setters
}
The following is my baseEntity clas
#MappedSuperclass
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected final Long id;
#Version
private Long version;
//Getters n setters
}
Thanks in advance.
I read everywhere and tried so many things for 5 hours.
I just want CompanyRepository to implement all 3 methods without me overriding them in some other class but if I have too then explain how because part of my code is dependent on CompanyRepository. I just wish to add save with update, please explain with respect to my code.
CrudRepository has only save but it acts as update as well.
When you do save on entity with empty id it will do a save.
When you do save on entity with existing id it will do an update that means that after you used findById for example and changed something in your object, you can call save on this object and it will actually do an update because after findById you get an object with populated id that exist in your DB.
save in CrudRepository can accept a single entity or Iterable of your entity type.
putting below if check resolve my issue and save method is working as save and update both when i pass id it updates the record in database and when i dont pass id it save new record in database
place is incoming object in my case and new place is new object of place in which i am setting the place id
if(place.getPlaceId()!=0)
newPlace.setPlaceId(place.getPlaceId());

Categories