I have a question that I dont really know where to start. So I thought i'd ask it here.
Basically, I have a drop down with names in it. I want these names to be in alphabetical order.
Populating the drop down happens as follows;
I query a database and pull down an Id and Name, make a object called "UserList", and set the name and id variables with what I get back.
I then add this object to an ArrayList. I do this over and over.
I then convert this collection to an array, and pass it to my JSP page using
session.setAttribute("userList", UserList);
I then populate the drop down as below.
<c:forEach items="${userList}" var="c" >
`<html-el:option value="${c.id}"><c:out value="${c.name}"/></html-el:option> </c:forEach>
There probably is a simple answer but how to I sort these names?
You usually do it by invoking public static Collections.sort(List<T> list) with your ArrayList as the parameter, but take care to implement the Comparable interface or it won't work (if they are Strings then it's already implemented):
Collections.sort(yourList);
Otherwise if you have a custom class but you want to sort just over some string field inside you can delegate the compareTo method:
public class User implements Comparable<User> {
public int compareTo(User other) {
return userName.compareTo(other.userName);
}
}
Finally if noone is your case just roll your own compareTo method, it should return -1, 0 or 1 if the the calling object is less, equal to or greater than the passed one.
Michael, you should be using a join and an order by to get this data from the database, not retrieving and sorting in Java:
select person.id, person.name from person inner join person_company using(personid)
order by person.name;
And not trying to sort and do this in java (the syntax above may not be perfect, my MySQL's a bit rusty).
I think the most ideal solution to this problem is to sort them at the database query level. If the table is well indexed, this will be the fastest and arguable the "best" practice.
That being said, if you just want to sort that list and not start mucking on the database side, use Collections.sort and have your UserList implement Comparable<UserList>
Be careful to use the case insensitive string comparison, or your users will likely not appreciate the sorting results.
I would change the database query to return the rows in the order you want them.
If you are using SQL the query could look like the following.
SELECT Id, Name FROM Person ORDER BY Name;
This has some advantages over doing it in code.
It is faster.
You do not have to change your code.
It is less work.
You can try and override the compareTo method for UserList so that it compares the elements in an alphabetical way according to some string value. Then, call the sort method of the array list and it should sort them using the compareTo you overrode.
You could sort in the database, instead of firing many queries with
select Id, Name from Person where id = '+variable+';
You can loop over the variables and make a comma separated list , this way you only fire a single query
int[] variableList = wherever_you_get_it_from;
StringBuffer csv = new StringBuffer();
for (int i = 0; i < variableList.length; i++) {
csv.append(variableList[i]);
if (i < variableList.length - 1)
csv.append(",");
}
then give
select Id, Name from Person where id in ( +csv.toString()+) order by Name;
Since you are getting the user ids from a company table, you can join and fetch
select u.usrid, u.name from company c left join user u on c.usrid = u.usrid
where c.companyid in (comapnyid1, companyid2, companyid3) order by u.name;
You should use Collections.sort and either of
Make your User class implement Comparable interface
Create a UserListComparator implementing Comparator<UserList> interface
See Comparable interface documentation for details.
Related
I have an object Person with the following fields:
firstName, secondName, age, nationallity, address, phoneNr.
And the list: ['John', 'Smith', '35', 'american', 'San Francisco', '+0324 235 327'].
I would like to put the values of the list into the object Person, without using the classic method of setting each value.
I want to avoid this:
person.setFirstName(list.get(0));
person.setSecondName(list.get(1));
person.setAge(list.get(2));
person.setNationallity(list.get(3));
person.setAddress(list.get(4));
person.setPhoneNumber(list.get(5));
My object has more fields than the ones I've put here as example (about 15), and I want to avoid writting a lot of code.
So my question is there a more elegant way of dumping all the values from the list into the object? I was thinking that in Java 8 maybe is there something but so far I haven't found anything.
There is no way to dump all values from the list into object, but you can add a new contructor in your Person Class, with a list as parameter like this :
public Person(List<String> list) {
this.firstName(list.get(0));
this.secondName(list.get(1));
this.age(list.get(2));
this.nationallity(list.get(3));
this.address(list.get(4));
this.phoneNumber(list.get(5));
}
and the call will be like :
Person person = new Person(list);
The other answer is good, there is one trick that can make it easier to work with though. This is especially relevant if there are a large number of entries such as the 15 mentioned in the question:
public Person(List<String> list) {
int index = 0;
this.firstName(index++);
this.secondName(index++);
this.age(index++);
this.nationallity(index++);
this.address(index++);
this.phoneNumber(index++);
}
Now you can add things to the list in any position or change the order or otherwise adjust it and don't have to manually update all the indexes. It also removes the risk of human error in accidentally getting an index wrong - although you do still need to get all the fields in the right order.
I'm replacing an application used at work, using Hibernate with an existing database. I can't modify the database since it's in use with other processes. When Hibernate pulls the main object from the db, the child objects are put in an unordered set. I've never really dealt with sets or sorting sets much before.
I need to display the last (chronologically) child for each set. There are no dates stored for the child objects, but since the id field in the db is AUTO_INCREMENT, I can sort them by id in lieu of a date.
One of the complaints about the existing system in use is that it's really, really slow. I'd like to show a definite increase of speed with the new application.
Given a Person object (variable name "off") with 0 to n "home addresses", I'm using:
Set addressSet = off.getAddresses();
List<Address> addressList = new ArrayList<>();
Iterator i = addressSet.iterator();
while(i.hasNext()){
addressList.add((Address) i.next());
}
Collections.sort(addressList, new AddressComparator());
Address a = null;
if(addressList.size()>0){
a = addressList.get(addressList.size()-1);
}else{
a = new Address(); //creates new Address object with empty strings
//for fields
}
My simple comparator is:
public int compare(Address t, Address t1) {
return t.getId().compareTo(t1.getId());
}
My question: Through either Java or Hibernate, is there a faster method to sort the sets?
From my point of view, you don't need to sort at all. Use
Collections.max()
or
Collections.min()
with your custom comparator provided to find the address you want. This has O(n) run time in worst case compared to O(nlog(n)) sorting time since you do not sort and only iterate your set once. The positive part also is that you don't need to convert your Set to List as the max and min methods work with any Collection instance.
Another advantage (at least for me) is that Collections utilities are part of the java runtime, so you don't need to add any third-party libraries.
I'm not sure if there are multiple sets, but from the code it seems like you are just getting the Address with the highest id. This can be achieved with the following sql, which wouldn't require sorting.
select * from table where id = (select max(id) from table);
You can do this without temporary List.
TreeSet sortedSet = Sets.newTreeSet(new AddressComparator());
sortedSet.addAll(off.getAddresses());
return sortedSet.first(); // or sortedSet.last() see what is suitable for you
Details on Sets.
UPD.
Please also see solution with Guava Ordering. It will allow you to get max element without temporary collection at all.
Ordering<Adress> ordering = Ordering.from(new AddressComparator());
return ordering.max(off.getAddresses());
You can sort at the database level in JPA/Hibernate by using the #OrderBy property where the sort is on a non-nested property. So in your case you can do this.
e.g.
#OneToMany
#OrderBy("id");
public Set<Address> addresses;
and Hibernate will ensure the collection is in a sorted set.
If the sort field happened to be on a nested property (which it isn't in your case) e.g. person.address.town.population then you can still have Hibernate deal with sort using the Hibernate specific (non-JPA) #Sort annotation which will ensure a sorted set as above but will sort using an in memory sort rather than a DB order by clause.
#OneToMany
#Sort(//natural or specify a comparator);
public Set<Address> addresses;
That does not get you the most recent address of course. If you don't want to change the mapping from Set to List which would allow you get the latest based on index, then you could also do this in the Database tier by various means e.g. by creating a view based on max address id for each person.
#Entity
#Table(name = "vw_most_recent_addresses"
public class MostRecentAddress extends Address{
}
public class Person{
#OneToMany
#OrderBy("id");
public Set<Address> addresses;
#OneToOne
public MostRecentAddress mostRecentAddress;
}
Since the play documentation on models is terrible I'll ask here. I have the basic code;
public static void Controller() {
List<Item> item = Item.find("SELECT itemname,id FROM Item WHERE itembool = true ORDER BY itemcreated ASC LIMIT 0,1").fetch();
if ( item == null ) {
notFound();
}
}
What I'm trying to do is get the value for 'itemname' returned for the first value returned from an SQL query (The real query is much more complicated and other things so it can't be replaced with methods). I can get the entire first object with item.get(0) but I can't figure out how to get the value of 'itemname' as a string and it doesn't seem to be documented anywhere.
Edit
Probably should have mentioned in the original question, I need to retrieve by field name, not index. I.E. I can't do items.get(0)[0]; I need to do items.get(0)['itemname'];
The documentation explains this if you read it, in here. Hibernate doesn't use SQL, but JPQL, which has a different syntax as it works with objects, not individual fields.
What you want to do can be achieved in two ways (both in the documentation):
List<Item> item = Item.find("SELECT i FROM Item i WHERE i.itembool = true ORDER BY i.itemcreated ASC").fetch(1);
List<Item> item = Item.find("itembool = true ORDER BY itemcreated ASC").fetch(1);
EDIT:
On the retrieval part, you will get a list of Item, so you can just access the field directly on the object:
item.get(0).getItemName();
Since Play uses Hibernate under the hood, you need to take a look at Hibernate's documentation.
In particular, SELECT itemname,id ... yields Object[] rather than Item, so that you can get itemname as follows:
List<Object[]> items = ...;
String itemname = items.get(0)[0];
well if you have to do a select itemname,id ..., you would not be able to do a items.get(0)["itemname"] because as axtavt and Pere have mentioned, you would get a Object[] back. You can instead create another (perhaps immutable) entity class that can be used in this query. Please refer to hibernate documentation for details. You can then model the entity based on your query requirements and use it to fetch information, thus letting hibernate handle all the magic number game for you. That ways, you would have a bean with filled up values that you can use to map back to your model class if you like.
HTH!
I have a query in Data Access Object DAOComments that joins users table and comments table and then store the result into Data Transfer Object DTOComments:
private static final String SQL_FIND_WITH_USERNAME =
"SELECT u.username, comments.* FROM users u JOIN comments
ON u.id = comments.id ORDER BY created_date DESC LIMIT 10;";
However, DTOComments does not have property "username" since it is the property of DTOUsers.
Option 1
So I decided to use Map<String, DTOComments>
In this case the map has username as KEY, DTOComments as VALUE.
But this approach will fails, because I care about the ORDER of result and that's why my query returns result in descending order. If I iterate the map on JSP page, the order is not consistent, so my JSP page would output the comment in random order.
(Even if order doesn't matter, I don't know if JSTL can display map's KEY. I know displaying the VALUE though)
Option 2
I could put the query result into ArrayList<DTOComments>
But I don't see any room to store the "username" now. Maybe I can add new property to DTOComments like private String username;
hmm... this would violate the concept of having DTO since it SHOULD reflect the database table schema.
Option 3
Create new class that hold all the information I need (ie. username + properties of DTOComments).
But just because I need one more property "username" in addition to the properties of DTOComments, creating new class seems not right way.
Could anyone give me advice how can I store all info returned by the query above in more organized way?
If I iterate the map on JSP page, the order is not consistent, so my JSP page would output the comment in random order.
That's the nature of HashMap. If you want to maintain insertion order in a Map, then you should be using LinkedHashMap instead. But the Map<User, Comment> approach has another disadvantage, if an user has posted more than one comment, you would be overwriting the previously inserted comment this way. You would like to use a Map<Comment, User> instead.
But IMHO it's better to make the User a property in Comment class, indicating a many-to-one relationship:
public class Comment {
private User user; // +getter +setter
}
This way you can end up with a List<Comment>.
That said, I wanted to comment on another statement of you:
I don't know if JSTL can display map's KEY. I know displaying the VALUE though)
You can iterate over a map using <c:forEach>. It goes over Map#entrySet(). Each iteration gives a Map.Entry object back which in turn has getKey() and getValue() methods.
Here's a kickoff example:
<c:forEach items="${map}" var="entry">
Key: ${entry.key}, value: ${entry.value}<br>
</c:forEach>
I have 65000 records of employees in a database . i am retreiving all the records and storing as employee object in a list as a cache. when customer enters the emp id in the browser , the record should be fetched from the list on one condition , without looping through the list. how can we acheive it.
using indexOf(Object ) we can acheive ,by implementing equals method , but what business logic should go in that.kindly let me know your views.
class Employee
{
private int id;
private String name;
Private String address;
public void setAddress (){}
public void setId(){}
public void setName(){}
// simillarly getMethods
}
1) I would implement a cache based on a hashmap rather than a list:
Map cache = new HashMap<Integer, Employee>();
This way you can retrieve an Employee object by a given ID very efficiently.
Additionally, I wouldn't add a setter for the employee id, since it can corrupt the mapping. Consider setting the id through a constructor parameter only.
--EDIT--
If you MUST use a list:
2) You may want to sort it first. This will allow performing a binary search (See Collections.binarySearch(..) methods). This requires implementing a Comparator or the Comparable interface, in order to define an ordering between the Employee objects. Also, you will have to create a dummy Employee object with the required id each time you want to perform the search.
3) If performance is not an issue, simply use List.indexOf(..). This requires implementing the equals(..) method in the Employee class.
4) In order to do it really without loops, you can create a sparse list, containing Employee with id N at index N. This is only feasible if the Employee id value range is not too big. The benefit is an optimal retrieval time.