-
1. Re: Suggestion: Seam control over EJB3 Query objects?
gavin.king Sep 30, 2005 6:37 PM (in response to rdewell)Maybe. Add a request to JIRA and I'll give it some thought when I get a chance.
-
2. Re: Suggestion: Seam control over EJB3 Query objects?
hookomjj Sep 30, 2005 10:24 PM (in response to rdewell)I've been thinking the same thing would be quite useful. I've always been a big fan of ColdFusion and what it could do with direct SQL use within the page. With EJB 3 and Seam as a flexible 'layer', managed DataModels as a better alternative to RowSets or ResultSets is much cooler/efficient.
-
3. Re: Suggestion: Seam control over EJB3 Query objects?
werpu Oct 2, 2005 10:42 AM (in response to rdewell)Well JSF would allow such an infrastructure... the Datamodel is the perfectly suited hook for such a thing...
-
4. Re: Suggestion: Seam control over EJB3 Query objects?
gavin.king Nov 11, 2005 6:46 PM (in response to rdewell)So, I put some thought into this last night. The best I could really come up with was something like the following:
@Name("bookingList") @NamedQuery(name="userBookingList", ejbql="from Booking b where b.user = :user order by b.checkinDate") public class BookingListAction implements Serializable { @In @Parameter private User user; @PageSize private int pageSize = 100; @Page private int page = 0; @Query(name="userBookingList", context="bookingDatabase") private Query query; @DataModel private List<Booking> bookings; @DataModelSelection private Booking booking; @Factory("bookings") public String execute() { bookings = query.getResultList(); return "bookings"; } public String nextPage() { page++; return null; } public String cancel() booking.cancel(); return null; } }
Which I'm not really sure is sufficiently better than:@Name("bookingList") @NamedQuery(name="userBookingList", ejbql="from Booking b where b.user = :user order by b.checkinDate") public class BookingListAction implements Serializable { @In(create=true) private EntityManager bookingDatabase; @In private User user; private int pageSize = 100; private int page = 0; @DataModel private List<Booking> bookings; @DataModelSelection private Booking booking; @Factory("bookings") public String execute() { bookings = bookingDatabase.createQuery("userBookingList") .setParameter("user", user) .setMaxResults(pageSize) .setFirstResult(page*pageSize) .getResultList(); return "bookings"; } public String nextPage() { page++; return null; } public String cancel() booking.cancel(); return null; } }
What do you guys think?
If anyone has better suggestions let me know. The only other thing I can really think of is to have a JSF component which just "does everything", which I think was more like what was envisaged in the original suggestion. But I'm not sure I want to throw things like that into the Seam core.
I guess one day, it would make sense to grow an add-on component library as a seperate package. If there are volunteers to actually start development on such a sub-project, let me know... -
5. Re: Suggestion: Seam control over EJB3 Query objects?
gavin.king Nov 11, 2005 6:46 PM (in response to rdewell)Note that the relevant JIRA issue is here:
http://jira.jboss.com/jira/browse/JBSEAM-87 -
6. Re: Suggestion: Seam control over EJB3 Query objects?
gavin.king Nov 11, 2005 7:00 PM (in response to rdewell)I guess I could go for this if it was actually a way to really remove the Query instance from the code completely. But then how do I decide when is the correct moment to actually execute the query?
I guess you could try something like this:@Name("bookingList") @NamedQuery(name="userBookingList", ejbql="from Booking b where b.user = :user order by b.checkinDate") public class BookingListAction implements Serializable { @In @Parameter private User user; @PageSize private int pageSize = 100; @Page private int page = 0; @QueryResult @DataModel private List<Booking> bookings; @DataModelSelection private Booking booking; @Factory("bookings") @ExecuteQuery(name="userBookingList", context="bookingDatabase") public String execute() { return "bookings"; } public String nextPage() { page++; return null; } public String cancel() booking.cancel(); return null; } } But @ExecuteQuery does not seem like a very kosher use of annotations....
-
7. Re: Suggestion: Seam control over EJB3 Query objects?
gavin.king Nov 11, 2005 7:08 PM (in response to rdewell)Hmmmmm ...... maybe something like this:
@Name("bookingList") @NamedQuery(name="userBookingList", ejbql="from Booking b where b.user = :user order by b.checkinDate") @QueryFactory(variable="bookings", query="userBookingList", context="bookingDatabase") public class BookingListAction implements Serializable { @In @Parameter private User user; @PageSize private int pageSize = 100; @Page private int page = 0; @QueryResult @DataModel private List<Booking> bookings; @DataModelSelection private Booking booking; public String nextPage() { page++; return null; } public String cancel() booking.cancel(); return null; } }
-
8. Re: Suggestion: Seam control over EJB3 Query objects?
rdewell Nov 11, 2005 7:11 PM (in response to rdewell)The last example you gave seems to be getting closer to the target... I'm concerned that relying on named queries may not be flexible enough. What about something like:
@QueryResult @DataModel private List<Booking> bookings; @DataModelQuery public Query createQueryPrototype(){ .. create the possibly dynamic query here from the session, filling it with info from the bean ... return query; }
Now, Seam needs to detect when: PageSize changes, current Page changes, other data in the model changes?
Then, it retrieves a new "query prototype", populates maxResults and first, executes, and populates "bookings" @DataModel/@QueryResult. -
9. Re: Suggestion: Seam control over EJB3 Query objects?
rdewell Nov 11, 2005 7:18 PM (in response to rdewell)IMO, your last example with @QueryFactory looks pretty nice. Very terse. I like that Seam could populate my query bindings via @Parameter as well.
I still think there would need be an alternate way to allow for a method to return a possibly dynamically created Query as well.
Does / would @Parameter have a name attribute so that you could match it up to the named var in the query string without relying on the name of the private variable? -
10. Re: Suggestion: Seam control over EJB3 Query objects?
gavin.king Nov 11, 2005 7:51 PM (in response to rdewell)OK, I'm converging on something like the following. Note that if we do this, it has to be able to work for both action-style event listeners and the factory stuff for GET requests.
This is "factory" style:@NamedQuery(name="userBookingList", ejbql="from Booking b where b.user.username = :user order by b.checkinDate") @Name("bookingList") @Scope(CONVERSATION) public class BookingListQuery implements Serializable { @In @Parameter User user; @QueryResult(query="userBookingList", context="bookingDatabase") @DataModel private List<Booking> bookings; @DataModelSelection private Booking booking; @PageSize private int pageSize = 100; @Page private int page = 0; @Factory("bookings") @Begin public void begin() {} public String nextPage() { page++; bookings = null; return null; } public String cancel() booking.cancel(); return null; } }
This is "event listener" style:@NamedQuery(name="userBookingList", ejbql="from Booking b where b.user = :user and b.checkinDate > :mindate order by b.checkinDate") @Name("bookingList") @Scope(CONVERSATION) public class BookingListQuery implements Serializable { @In @Parameter private User user; @Parameter Date mindate; public void setMindate(Date mindate) { this.mindate = mindate; } @QueryResult(query="userBookingList", context="bookingDatabase") @DataModel private List<Booking> bookings; @DataModelSelection private Booking booking; @PageSize private int pageSize = 100; @Page private int page = 0; @Begin public String begin() { return "bookings"; } public String nextPage() { page++; bookings = null; return null; } public String cancel() booking.cancel(); return null; } }
The idea is that the query would be executed at the end of any method during the INVOKE_APPLICATION or RENDER_RESPONSE phases where the @QueryResult attribute is null.
Only problem with that idea is that you would not get to actually do anything with the query result list in the code. I'm trying to figure out if that is a showstopper or not.... -
11. Re: Suggestion: Seam control over EJB3 Query objects?
gavin.king Nov 11, 2005 7:56 PM (in response to rdewell)"rdewell" wrote:
I still think there would need be an alternate way to allow for a method to return a possibly dynamically created Query as well.
Sure, that is a trivial extension. But I want to get it right for the static query case first."rdewell" wrote:
Does / would @Parameter have a name attribute so that you could match it up to the named var in the query string without relying on the name of the private variable?
Sure, of course. -
12. Re: Suggestion: Seam control over EJB3 Query objects?
gavin.king Nov 12, 2005 12:51 AM (in response to rdewell)So, I implemented this stuff, but I don't think I'll commit it. I think it has a tendency to make code less readable rather than mode readable, and doesn't really reduce LOC.
Here is HotelBookingAction after migrating to this stuff:@NamedQuery(name="findHotels",
queryString="from Hotel where lower(name) like :search or lower(city) like :search or lower(zip) like :search or lower(address) like :search")
@Stateful
@Name("hotelBooking")
@Interceptor(SeamInterceptor.class)
@Conversational(ifNotBegunOutcome="main")
@LoggedIn
public class HotelBookingAction implements HotelBooking, Serializable
{
private static final Logger log = Logger.getLogger(HotelBooking.class);
@PersistenceContext(type=EXTENDED)
private EntityManager em;
private String searchString;
@QueryParameter
private String search;
@QueryPageSize
private int pageSize = 50;
@QueryResult(namedQuery="findHotels",
persistenceUnit="bookingDatabase")
@DataModel
private List<Hotel> hotels;
@DataModelSelectionIndex
private int hotelIndex;
@Out(required=false)
private Hotel hotel;
@In(required=false)
@Out(required=false)
@Valid
private Booking booking;
@In
private User user;
@In
private transient FacesContext facesContext;
@In(required=false)
private BookingList bookingList;
@Begin
public String find()
{
hotel = null;
hotels = null;
search = searchString==null ? "%" : '%' + searchString.toLowerCase().replace('*', '%') + '%';
return "main";
}
public String getSearchString()
{
return searchString;
}
public void setSearchString(String searchString)
{
this.searchString = searchString;
}
public String selectHotel()
{
if ( hotels==null ) return "main";
setHotel();
return "selected";
}
public String nextHotel()
{
if ( hotelIndex<hotels.size()-1 )
{
++hotelIndex;
setHotel();
}
return null;
}
public String lastHotel()
{
if (hotelIndex>0)
{
--hotelIndex;
setHotel();
}
return null;
}
private void setHotel()
{
hotel = hotels.get(hotelIndex);
log.info( hotelIndex + "=>" + hotel );
}
public String bookHotel()
{
if (hotel==null) return "main";
booking = new Booking(hotel, user);
booking.setCheckinDate( new Date() );
booking.setCheckoutDate( new Date() );
return "book";
}
@IfInvalid(outcome=REDISPLAY)
public String setBookingDetails()
{
if (booking==null || hotel==null) return "main";
if ( !booking.getCheckinDate().before( booking.getCheckoutDate() ) )
{
log.info("invalid booking dates");
FacesMessage facesMessage = new FacesMessage("Check out date must be later than check in date");
facesContext.addMessage(null, facesMessage);
return null;
}
else
{
log.info("valid booking");
return "success";
}
}
@End
public String confirm()
{
if (booking==null || hotel==null) return "main";
em.persist(booking);
if (bookingList!=null) bookingList.refresh();
log.info("booking confirmed");
return "confirmed";
}
@Destroy @Remove
public void destroy() {
log.info("destroyed");
}
}
I'm not seeing that this is an improvement. Rather, it's smearing out a well-defined concern and making it more difficult to see what is the flow of execution. -
13. Re: Suggestion: Seam control over EJB3 Query objects?
marius.oancea Nov 12, 2005 5:58 AM (in response to rdewell)Hmm. Ok very nice idea but don't you think will be the same problem as with @DataModel ? (Only one per component).
So I think:
@Parameter, @Page, @PageSize has to know to wich query referes.