I have a table client having a column cid.
The entity class is
public class client{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version;
#NotNull
#ManyToOne
private Client cid;
....
}
In my session I have kept the client id and retrieving by the following way
String clientId = request.getSession().getAttribute("clientId").toString();
This is my controller code
List l=serviceClientServiceImpl.getSavedParents(clientId);
uiModel.addAttribute("savedParents",l);
This is my getSavedParents code
public List getSavedParents(String cid)
{
List l=serviceClientDaoImpl.getSavedParents(cid);
return l;
}
and this is my query code
public List getSavedParents(String cid)
{
String queryString="select distinct pid from ClientParentQuestion where cid="+cid;
Query query=entityManagerUtil.getQuery(queryString);
return query.getResultList();
}
when I try to receive in my jsp using ${savedParents} then I get nothing.
I do not understand where do I make mistake.
Can any body solve my problem?
Your code is vulnerable to SQL injection.
So client has a self-reference to itself.
I'd refactor the dao method to:
public List getSavedParents(String cid) {
return getEntityManager()
.createQuery("select distinct cpq.pid from ClientParentQuestion cpq where cpq.cid.id = :cid")
.setParameter("cid, Long.valueOf(cid))
.getResultList();
}
Also use an alias for the entity when you want to specify what properties you'd like to return or match.
It's much more readable and it follows the JPQL/HQL projection idioms.
Related
I have a user that can have a wallet. Now, when user create a wallet I want to give him a option to create a transaction on that wallet. So, on that form I would like to have next fields, so prompt user to insert:
Amount of transaction, Date, note of transaction, category of transaction
So far I have this in Spring:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "transaction_id")
private Integer id;
private double amount;
private String note;
#DateTimeFormat(pattern = "dd-MM-yyyy")
#Column(name = "date")
private Date date;
But I have problem with field category. I want to prompt user to pick from dropdown menu one of category from the list. But how to create a field categories that will be filled with names of categories?
I tried with:
#Column(name = "categories")
private List categories;
But I'm getting:
Could not determine type for: java.util.List, at table: transaction, for columns: [org.hibernate.mapping.Column(categories)]
Spring JPA lets you handle this a couple of different ways. Since you didn't specify what type of thing a category is, I am assuming you want it to be a String.
#ElementCollection
The first, easiest, and generally recommended way is to use the #ElementCollection. If your categories are fairly well fixed, you can even use enums for this.
#ElementCollection
private List<String> categories=new ArrayList<>();
With this, JPA will generate a second table in the database called transaction_categories You don't have to create an Entity for this table or anything. Everything is handled under the covers.
JsonB
If you are using postgres 10+ (I think) you can make a column into a JSON object. You will need the following dependency in your gradle.
implementation 'com.vladmihalcea:hibernate-types-52:2.15.1'
And you will need to change your model thus:
#TypeDefs({
#TypeDef(name = "json", typeClass = JsonType.class)
})
#Entity
public class Transaction {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "transaction_id")
private Integer id;
private double amount;
private String note;
#DateTimeFormat(pattern = "dd-MM-yyyy")
#Column(name = "date")
private Date date;
#Type(type = "json")
#Column(columnDefinition = "jsonb")
private List<String> categories=new ArrayList<>();
}
So long as this list does not become gigantic, it is a pretty decent solution.
Something as simple as a MySQL native query like this:
SELECT image_filename FROM article
using this entity
#Entity
#Table(name = "article")
public class Article {
#Id
#Column(name = "id")
private String id;
#Column(name = "title")
private String title;
#Column(name = "summary")
private String summary;
#Column(name = "content")
private String content;
#Column(name = "image_filename")
private String imageFilename;
}
returns the data from a different field called summary. I've checked the SQL table and it does not have this issue.
The issue occurs in all sorts of other queries. What would cause this?
I was able to solve this problem by making sure the columns in my create table statement, entity, result set mapping, and native query were all in alphabetical order (except for the primary key, which is first).
It seems very strange to me that JPA would require this, and will jumble up the columns if you don't do this.
For the relationship of many-to-one, one-to-many or even many-to-many, how to get an object without the objects included on the other side?
Say a group of address and a group of people, the relationship would be many-to-many, what if i just wanne get a "people" without the concerned address?
Classroom.java
#Entity
public class Classroom {
public Classroom(){
}
public Classroom(long id, String name) {
this.id = id;
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "classroom_people",
joinColumns = {#JoinColumn(name = "classroom_id", referencedColumnName = "id") },
inverseJoinColumns = {#JoinColumn(name = "people_id", referencedColumnName = "id") })
private List<People> peoples = new ArrayList<>();
...
// getter() and setter()
}
People.java
#Entity
public class People{
public People(){
}
public People(long id, String name) {
this.id = id;
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
private long id;
#Column(name = "name")
private String name;
#ManyToMany(mappedBy = "peoples", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Classroom> classrooms = new ArrayList<>();
...
// getter() and setter()
}
By using hibernate, the table of Classroom, People and the join table classroom_people have been created automatically. Until now, everything is fine.
Then by using spring repository, I tried to fetch a people by passing the name, and return a JSON object.
What I hope to get is just a JSON including people.id and people.name but what I get is a JSON including one people and all the classroom in the Set, which includes all the concerning peoples.
SO I guess the problem is the List and List and what I should do is to create three tables: classroom, people and classroom_people, then store the relationship manually to join table. Then I can fetch a simple object without problem.
AM I right?
I use Spring Repository and findAllByName(), so the SQL is generated by spring. Just wonder whether this would be the source of problem.
It's easily done with HQL if you keep the relationships bidirectional.
Something like:
String hql = "select addr.people from " + Address.class.getName() + " addr where addr.name(or some other attribute) = :addressName"
//set parameter "addressName"
EDIT:
Now you took a different example, but the above query applies the same, for example getting people from a classroom.
About your last note, getting a People by name will fetch only People instance, because the #ManyToMany List referencing classrooms is LAZY which means is not populated until you call people.getClassRooms()(if you have the session open it will load the empty list with another query)
You don't need to create the junction table yourselves, not for this purpose.
How do you know that List<ClassRoom> is populated when you fetch a People? Because from your mappings it looks OK, unless you're calling people.getClassRooms()
I've got two entity objects in my database: UserEntity and ItemEntity and they're mapped with OneToMany relationship.
Here is my code:
UserEntity:
#Entity
#Table(name = "users")
public class UserEntity {
#Id
#Column(name = "user_id")
#GeneratedValue
public int user_id;
#Column(name = "userlogin")
public String userlogin;
#Column(name = "userpass")
public String userpass;
#Column(name = "name")
public String name;
#Column(name = "email")
public String email;
....
#JsonBackReference
#OneToMany(mappedBy="user", cascade = { CascadeType.MERGE },fetch=FetchType.EAGER)
private List<ItemEntity> items;
ItemEntity:
#Entity
#Table(name = "items")
public class ItemEntity {
#Id
#Column(name = "id")
#GeneratedValue
private int id;
#Column(name = "title")
public String title;
#Column(name = "info")
public String info;
#JsonManagedReference
#ManyToOne
#JoinColumn(name="user_id")
private UserEntity user;
And now I'm trying to read all my Items from my database with specific fields from users that owns current item. I need only UserEntity name and email.
This code:
Query query = this.sessionFactory.getCurrentSession().createQuery("from ItemEntity WHERE title = :title");
returns all fields from UserEntity also, because it's mapped, but I don't want that, because I'm sending that data as JSON, and someone can see all informations about user who own that item (like user login and password) in some dev tools like Chrome.
How to reach that?
I'd suggest you use DTO.
Covert your entities to DTO and then transform the DTO objects to
json string.
In the DTO populate only those field that you want as part of your response.
This would make your design more clean.
In addition to what's jitsonfire is suggesting, you can write a query like this
select name, email from ItemEntity WHERE title = :title
than get your results like
List<Object[]> result = query.list();
The object array will contain your columns, the list element will equal to rows, so you can do something like
for (Object[] tuple : result) {
tuple[0]; //name
tuple[1]; // email
}
I'm using Play! 2.1.4 framework with Ebean/MySQL.
The system basically keeps track of courses, and their requirements. Eg, if you want to take Advanced Art, you must first take regular Art. This is a ManyToMany Relationship, as I understand, but I could be wrong.
Here's my model:
#Entity
public class Course {
public static Model.Finder<Long,Course> find = new Model.Finder<Long,Course>(Long.class, Course.class);
#Id
private Long id;
private String name;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable( name = "course_course",
joinColumns = #JoinColumn(name = "course_id1"),
inverseJoinColumns = #JoinColumn(name = "course_id2"))
private Set<Course> courseRequirements = new HashSet<Course>();
private String availability;
private Long category;
#Lob
private String description;
#Lob
private String otherRequirements;
private Long bucketId;
// Getters and setters...
Here's how I return the JSON to my frontend:
List<Course> courses = Ebean.find(Course.class).fetch("courseRequirements").findList();
JsonContext jsonContext = Ebean.createJsonContext();
return ok(jsonContext.toJsonString(courses));
However, in the "courseRequirements" key in the JSON, it opens up a new array with full course objects in it. I just want to get an array of the course IDs it requires, for example:
courseRequirements: [3, 5]
means that: this course requires you to take courses with ID 3 and 5 first.
I rewrote my getter to this:
public Set getCourseRequirements() {
Set requiredCourseIds = new HashSet();
for(Course course : courseRequirements)
{
requiredCourseIds.add(course.getId());
}
return requiredCourseIds;
}
because I thought that Ebean will pull that when trying to fill in the JSON key, but that's not the case.
How can I change it so that it only returns an array of course IDs for the courseRequirements key, instead of full objects?
Thanks!
Instead of toJsonString(Object o) you should use toJsonString(Object o, boolean pretty, JsonWriteOptions options).
With JsonWriteOptions "You can explicitly state which properties to include in the JSON output for the root level and each path."
http://www.avaje.org/static/javadoc/pub/com/avaje/ebean/text/json/JsonWriteOptions.html