Create controller for "generic" path mapping springboot - java

Let's assume I have many teams like : "Team A, Team B...Team Z" and each team has at least 5 components. Now I want to create a generic controller that responds to any request of type /team/number so I can be able to get informations about a team member.
For example my controller must be able to map this request :
#RequestMapping(value = "/Team A/1", method = RequestMethod.GET)
Team class could be :
#Entity
#Table(name = "team")
public class Team {
public Team() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private List<Player> players;
}
And
Player :
#Entity
#Table(name = "player")
public class Player {
public Player() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int number;
}
Obviously it can either execute GET and POST. The point is that I don't want to specify a controller for each team and each number, I just want one that can respond to /String/int .
I need also to specify which string it can accept and the range of values (max 5 for example).

The #PathVariable spring annotation would help you in this case.
For example:
#RequestMapping(value = "{team}/{component}", method = RequestMethod.GET)
public Team getTeam(#PathVariable String team, #PathVariable int component) {
//Use team and component here
}
You can just add some logic into the controller method to throw some exception if either variable is an unexpected value.
Though depending on what you are trying to achieve, this may be a bad design as this controller method will catch all GET requests that match String/int. You may want to try something like this to make the request mapping more specific:
#RequestMapping(value = "team/{teamLetter}/{component}", method = RequestMethod.GET)
public Team getTeam(#PathVariable char teamLetter, #PathVariable int component) {
//Use team and component here
}

Related

Java Spring Neo4jRepository unable to read a property of abstract type

I have just started experimenting with Neo4J/Java and expect this is an easy one I'm missing, and probably phrasing my queries wrong.
I have some model classes as follows:
#Node
public class Garment {
#Id
#GeneratedValue
private Long id;
private String name;
#Relationship(type = "DESIGNED_BY")
private Entity designer;
// Other properties getters/setters removed for readibility
public Entity getDesigner() {
return designer;
}
public void setDesigner(Entity designer) {
this.designer = designer;
}
}
public abstract class Entity {
#Id
#GeneratedValue
Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Node
public class Person extends Entity {
}
#Node
public class Company extends Entity {
}
And a corresponding repository
public interface Garment extends Neo4jRepository<Garment, Long> {
Garment findByName(String name);
}
I have no problem inserting, using repository.save(), this correctly adds everything; nodes, relationships. Fine. It gives Designers of type Person labels of Person, and Designers of type Company the label Company.
However, when I do a find, e.g. findByName(), findAll(). it is not matching the designer and just saying designer is null, according to the cipher being executed/logged it looks like it's trying to build a relationship there with nodes with an Entity label, which there are none.
How can I get my repository to return Garments with designers of Person and Companys. I expect this is going to be as simple as an annotation, in order to fix.
(Note I've tried adding a #Node on the entity type with Person and Company as labels, however it just results in every node being added as both a Person and a Company).

How to load any property files at compile time with spring?

I want to load any property files in spring POJO classes.
Actually, I want to use any properties from property file in #Size, #NotNull or any validation annotations in spring model classes.
But the issue is that #Size, #NotNull etc annotation are invoked at compile-time whereas any property file's data are invoked at runtime.
Even, I want to use in the max parameter of #Size which accepts the only constant integer. How can I load value and cast it to Integer as a constant value?
And I can use custom validator or any custom annotation to solve the issue but as per the organization policy, I can't use much customization.
I found an additional solution to this problem that we can use a class of constants instead of the property file.
I can use this constants file anywhere in my workspace whereas property file can't use at Size.max property of the model validation
CommonConstants.java
public class CommonConstants
{
public static final int NAME=4;
public static final int ROLE=2;
}
Employee.java
private int id;
#Size(max = CommonConstants.NAME, message = "length exceeds : name")
private String name;
#Size(max = CommonConstants.ROLE, message = "length exceeds : role")
private String role;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
EmployeeController.java
#Controller
public class EmployeeController {
private static final Logger logger = LoggerFactory.getLogger(EmployeeController.class);
private Map<Integer, Employee> emps = null;
public EmployeeController() {
emps = new HashMap<Integer, Employee>();
}
#RequestMapping(value = "/emp/save.do", method = RequestMethod.POST)
public String saveEmployeeAction(#Valid Employee employee, BindingResult bindingResult, Model model)
{
if (bindingResult.hasErrors()) {
logger.info("Returning empSave.jsp page");
return "empSave";
}
logger.info("Returning empSaveSuccess.jsp page");
model.addAttribute("emp", employee);
emps.put(employee.getId(), employee);
return "empSaveSuccess";
}
}
Output

How to design database model and use save method of JpaRepository to save entity if an entity has List<Items> in it?

JAVA SPRING :I am exploring JPA and am not sure of optimized way to design db and using save() of repository to save entity data right away into DB. Specifically, I have a basic class viz. Movie -
package com.kurshit.moviesmgr.vo;
import java.util.List;
public class Movie {
long movieId;
String title;
String yearOfRelease;
List<String> genere;
public Movie(long movieId, String title, String yearOfRelease, List<String> genere) {
super();
this.movieId = movieId;
this.title = title;
this.yearOfRelease = yearOfRelease;
this.genere = genere;
}
public Movie() {
// TODO Auto-generated constructor stub
}
public long getMovieId() {
return movieId;
}
public void setMovieId(long movieId) {
this.movieId = movieId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getYearOfRelease() {
return yearOfRelease;
}
public void setYearOfRelease(String yearOfRelease) {
this.yearOfRelease = yearOfRelease;
}
public List<String> getGenere() {
return genere;
}
public void setGenere(List<String> genere) {
this.genere = genere;
}
}
Each movie has list of Genere - List- it falls under, like - Action, Comedy, etc.
I am trying to create an interface that extends JpaRepository and use the inbuilt save method to save the Movie Data into DB.
I am not sure about how I should design my DB - As in, Questions like -
1. Shall I create two different tables for Movie and Genere wherein Movie table references to Genere ?
2. Shall I create just onw table and store all Genere's list as a single CSV in one column ?
3. Can I use repository's save() right away to save and map this data into respective tables.
Would really appreciate if someone can share any sources or sample code to refer or can offer any help.
Thanks much!
First of all, you should search look up #Entity annotation so that you can tell your ORM to create the necesary table for that entity.
Secondly, you need to ask yourself, how this application will work. It would be best in my opinion to create a genre entity as well, linked to Movie through a #ManyToMany relationship.
Try looking over the simple entity example here
https://spring.io/guides/gs/accessing-data-jpa/
First variant - with 'genre' as enum (if your genre is a fixed list):
#Data // it's Lombok annotation: https://projectlombok.org/features/Data
#NoArgsConstructor
#Entity
public class Movie implements Serializable {
#Id #GeneratedValue
private Integer id;
private String title;
private Integer yearOfRelease;
#ElementCollection
#Enumerated(EnumType.STRING)
#Column(name = "genre")
private List<Genre> genres;
public Movie(String title, Integer yearOfRelease, List<Genre> genres) {
this.title = title;
this.yearOfRelease = yearOfRelease;
this.genres = genres;
}
}
public enum Genre {
ACTION, COMEDY, ...;
}
public interface MovieRepo extends JpaRepository<Movie, Integer> {
}
In this case you create your movie like this:
Movie movie = new Movie("Title", 2000, Arrays.asList(ACTION, COMEDY));
movieRepo.save(movie);
Second variant - 'genre' as independent entity:
#Data
#NoArgsConstructor
#Entity
public class Movie implements Serializable {
// the same as previous one...
#ManyToMany
private List<Genre> genres;
}
#Data
#NoArgsConstructor
#Entity
public class Genre implements Serializable {
#Id private String name;
public Genre(String name) {
this.name = name
}
}
public interface MovieRepo extends JpaRepository<Movie, Integer> {
}
public interface GenreRepo extends JpaRepository<Genre, String> {
}
In this case you first create genres:
List<Genre> genres = genreRepo.saveAll(Arrays.asList(
new Genre("Action"),
new Genre("Comedy"),
));
Then create movie:
Movie movie = new Movie("Title", 2000, genres);
movieRepo.save(movie);
More info to read: Hibernate ORM User Guide - Collections

Auto Increment like with String values in hibernate

Well i want to know if there is a much appropriate way to tackle generating auto id with string values, my first idea is creating an auto increment id which we can call auto_id then before saving a new entity I'll query for the latest data inside the db to get the id then I'll add 1 to my auto generate value column that I assign name which is stringValue+(id+1) though I'm concerned on how it will affect the performance as to saving this entity needs two access in db which is fetching and saving... like my question earlier is there a much appropriate way to handle this scenario?
And also sorry for my English guys if you want to clarify things with my question kindly ask, thnx in advance..
Here's my code for AttributeModel for hibernate annotation
#Component
#Entity
#Table(name="attribute_info")
public class AttributeModel {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="attr_id", nullable=false, unique=true)
private int id;
#Column(name="attr_name")
private String name;
#Column(name="attr_desc")
private String desc;
#Column(name="attr_active")
private int active;
#Column(name="attr_abbr")
private String abbr;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name="stats_id", referencedColumnName="stats_id")
private BaseStatisticModel baseStats;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public int getActive() {
return active;
}
public void setActive(int active) {
this.active = active;
}
public String getAbbr() {
return abbr;
}
public void setAbbr(String abbr) {
this.abbr = abbr;
}
public BaseStatisticModel getBaseStats() {
return baseStats;
}
public void setBaseStats(BaseStatisticModel baseStats) {
this.baseStats = baseStats;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
I can only say "Don't do it". How is a String ID like "str10001" better than 10001? It can't be an optimization as strings take more memory and more time. So I guess you need to pass it to some String-expecting method later.
If so, then pass "str" + id instead. Constructing the string on the fly surely won't saturate your server.
If not, then let us know what you actually need rather than what you think it could help you to achieve it.
I'm pretty sure, Hibernate can't do it. It couldn't some long time ago I checked it recently and it makes no sense (in any case, it's not a feature crowds would request).

Save entity after unique constraint exception

i have an entity with a unique name.
In my example i save two persons with the same name. At the second time comes an "EntityExists" (Unique) Exception, that was the expected behavior.
After it i changed name and set the "ID" to null.
Than i try to persist it again but i get "org.apache.openjpa.persistence.EntityExistsException: Attempt to persist detached object "com.Person#1117a20". If this is a new instance, make sure any version and/or auto-generated primary key fields are null/default when persisting.
without the version it works but i find no solution to "reset" the version number.
Can someone help me?
Update: My new problem is, that i have a base entity an two pcVersionInit (look at my answer at bottom) i can't override it, i tried it in base and normal entity what is the best practise now instead of "override" the value in pcVersionInit ? Copy Constructor?"
public class Starter{
private static EntityManager em;
public static void main(String[] args) {
em = Persistence.createEntityManagerFactory("openjpa")
.createEntityManager();
Person p1 = new Person("TEST");
savePerson(p1);
Person p2 = null;
try{
p2 = new Person("TEST");
savePerson(p2);
}catch(Exception e){
p2.setId(null);
p2.setName(p2.getName()+"2");
em.persist(p2);
}
}
private static void savePerson(Person person){
em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
}
}
Person.class:
#Entity
public class Person implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator="seqGenerator")
#SequenceGenerator(name="seqGenerator",sequenceName="personSeq")
private Long id;
#Version
private Long version;
#Column(nullable=true, unique=true)
private String name;
public Person(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
}
First off, stay away from messing around with pcVersionInit. I'd suggest to create a copy constructor in your Person Entity and in the event of rollback create a new one using the copy constructor.
Okay the problem is that OpenJPA adds a field named pcVersionInit (with #version) and set it "true" after try to persist. If i use reflection to set it to false, it works. Other way is a copy constructor.

Categories