I have a database table displayed through Datatable (Primefaces 3.4.2) and I want to show a comboFilter in the header populated with values from the database table itself.
1) Since these values are not a PK or FK, I built a named query to retrieve distinct values for the prefDep column:
#NamedQuery(name = "Upb.findPrefDeps", query = "SELECT DISTINCT u FROM Upb u WHERE u.prefDep = :prefDep")
2) In my AbstractController:
public List<T> getPrefDepsList() {
if (prefDeps == null) {
prefDeps = this.ejbFacade.findPrefDeps();
}
return prefDeps;
}
3) As I inject the facade EJB, how can I build a managed bean property to be used in the filterOption below?
The managedBean:
#ManagedBean(name = "upbController")
#ViewScoped
public class UpbController extends AbstractController<Upb> implements Serializable {
#EJB
private UpbFacade ejbFacade;
public UpbController() {
super(Upb.class);
}
#PostConstruct
public void init() {
super.setFacade(ejbFacade);
}
public SelectItem[] getPrefDepOptions() {
return prefDepOptions; //build/populate this
}
}
The jsf:
<p:column filterBy="prefdep" headerText="PrefDep"
filterOptions="#{upbController.prefDepOptions}"
filterMatchMode="exact">
<h:outputText value="#{item.prefDep}" />
</p:column>
Thanks in advance.
I do not know if I understand your question the right way. You want to execute the named query once and store the distinct values in a property in the managed bean? For that you can use a PreRenderView event which would be called before rendering the page. You can call a init-function to load such values with this event.
You can also access the getter with the named query, but this may be called not only once.
Related
I have a SpringBoot 2.6.11 application with JPA 2.2.
I have an entity like this:
#Data
#Entity
#Table(name = "entity")
public class Entity implements Serializable {
....
#Convert(converter = ListConverter.class)
private List<String> referenceCode;
....
}
I have this Converter:
#Converter(autoApply = true)
public class ListConverter implements AttributeConverter<List<String>, String> {
#Override
public String convertToDatabaseColumn(List<String> attribute) {
return String.join(";", attribute);
}
#Override
public List<String> convertToEntityAttribute(String dbData) {
return new ArrayList<>(Arrays.asList(dbData.split(";")));
}
}
And when I insert or extract this element all working fine. But now I wanna query that element and I don't know how to do it. If I do something like that:
public List<Entity> findByReferenceCode(String reference);
It doesn't work, if I do:
#Query("select e from Entity e where e.referenceCode IN ?1")
public List<Entity> findByReferenceCode(List<String> reference);
Still doesn't work..
The only way I found is by the nativeQuery but is really an extrema ratio. Ho can I solve this?
Thank you
To really do what you want here, you need to use an #ElementCollection. The reason being that there is no reliable way for JPA to query a single column and treat it as a collection. Reliably querying a collection requires a second table (which is what #ElementCollection does). You can continue to use the #Converter, but your queries will have to be customized to handle the disparity between the entity attribute type (list) and the actual database column type (string).
If you are okay with the limitations of the #Converter then it's fine (I have used them this way) but if you truly need to query the attribute like a collection (e.g. search for multiple independent items, perform counts, aggregations, etc) and you want those queries to be generated by a JPA layer, then you will have to use #ElementCollection and let it create a second table.
This is rows in database.
group userid
1 a
2 b
1 c
3 d
I want to query using #query annotation about
#Query(value ="SELECT *, count(*) as cnt FROM user group by body order by cnt DESC", nativeQuery = true)
List<User> getGroupRanking();
and, i expect to result about
group cnt
1 2
2 1
3 d
but List<User> collection is not accept to 'cnt' attribute which was alias.
another reason not to accept, User defined class was not containing 'cnt' attribute. i was defined 'cnt' attribute in User Class like following code.
public class User{
#Column(name ="group")
private String group;
..
private Integer cnt;
public Integer getCnt(){ return cnt; }
public void setCnt(Integer cnt){ this.cnt = cnt; }
}
Although I will prevent to update the schema, The above code update the schema. because
appConfig.xml configuration was set like this
<prop key="hibernate.hbm2ddl.auto">update</prop>
i don't want to update schema, and i want to display cnt attribute in JSP file through User Class(bean) when i using #Query annotation. how to accept a 'cnt' attribute using the User class without update schema?
'as' keyword usage not containing in detail(link)
It's difficult to understand what you need, but I'll try... ))
First create a projection:
public interface UserCount {
User getUser();
long getUserCount();
}
Then create a repository query method which return this projection:
public interface UserRepo extends JpaRepository<User, Long> {
#Query("select u as user, count(u) as userCount from User u group by u order by userCount desc")
List<UserCount> getUserCounts();
}
Then you can get list of UserCount projections and send it to your JSP view.
Additional reading:
Projections on repository query methods
Data transfer object
UPDATE
Controller:
#Controller
public class UserController {
#Autoware
private UserRepo repo;
#GetMapping("/")
public String withCount(Model model) {
List<UserCount> userCounts = getUserCounts();
model.addAttribute("userCounts", userCounts);
return "userCounts";
}
}
JSP
<table>
...
<c:forEach items="${userCounts}" var="item">
<tr>
<td>${item.user.group}</td>
...
<td>${item.userCount}</td>
</tr>
</c:forEach>
</table>
Some info: 1, 2
I recommend to switch from jsp to thymeleaf.
I am working on Spring Boot Where i am facing issue while fetching values from a particular column and use it as return value in another class. I had followed the steps as
1) fetching the value using repository in service class as below
public MyEntity fetchDate(){
return MyRepository.findByName(date)
}
2) How can I write a method in controller class which returns fromDate value which I passed in findByName() method as
public Date getDate(Long Id){
myService.fetchDate();
return date;
}
MyEntity.getFromDate () can return the required value.
I believe you should be able to override the query behavior with the #Query annotation on your interface as follows and it should provide the functionality you're interested in:
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
#Query("SELECT e.myDateField FROM MyEntity e WHERE e.name = :name")
Date findByName(String name)
}
Good evening everybody, this is my first post on Stack Overflow.
I have been quite recently introduced to Java 6 EE and, in particular, to JPA as part of the JSF 2.1 framework and I am now facing a strange behavior that I would like you to help me understand.
In our project (developed using NetBeans 7.2) we have several one-to-many relationship and we would like to navigate them the same way we navigate many-to-one ones. The fact is that, instead, we are able to make them work as we want only after having restarted the application server (Glassfish 3.1.2) and, in addition, this behavior lasts only till the next deployment; which means we need to restart Glassfish every time we apply a modification...
Here are some code excerpts to help you understand our situation.
This represents our main entity (Person) that has, among the others, a one-to-many relationship with Email as well as with Phone and a many-to-one relationship with AccountType
#Entity
public class Person implements Serializable {
//
//private non-collection fields including id
//
#OneToMany(mappedBy="person", fetch=FetchType.EAGER)
private Collection<Email> personEmails;
#OneToMany(mappedBy="person")
private Collection<Phone> personPhones;
#ManyToOne
private AccountType accountType;
//
// getter and setter, hashCode, isEqual and toString
//
}
And these are Email...
#Entity
public class Email implements Serializable {
//
//private non-collection fields including id
//
private String address;
#ManyToOne
private Person person;
//
// getter and setter, hashCode, isEqual and toString
//
}
... Phone ...
#Entity
public class Phone implements Serializable {
//
//private non-collection fields including id
//
private String number;
#ManyToOne
private Person person;
//
// getter and setter, hashCode, isEqual and toString
//
}
... and AccountType
#Entity
public class AccounType implements Serializable {
//
//private non-collection fields including id
//
private String name;
#OneToMany(mappedBy="accountType")
private Collection<Person> persons;
//
// getter and setter, hashCode, isEqual and toString
//
}
We have then set up a sample page to test how that three fields in Person are actually fetched.
This represents the xhtml page...
<h:form id="form">
<h:panelGrid columns="2">
<h:outputLabel value="forename" />
<h:outputLabel value="#{meBean.currentUser.forename}" />
<h:outputLabel value="emails" />
<h:outputLabel value="#{meBean.currentUser.personEmails.size()}" />
<h:outputLabel value="phones" />
<h:outputLabel value="#{meBean.currentUser.personPhones}" />
<h:outputLabel value="accountType" />
<h:outputLabel value="#{meBean.currentUser.accountType.name}" />
</h:panelGrid>
</h:form>
... and this the controller
#ManagedBean
#RequestScoped
public class MeBean {
#EJB
private PersonFacade personFacade;
private Person currentUser;
public MeBean() {
init();
}
#PostConstruct
private void init() {
// Hard-coding user details
try {
this.currentUser = this.personFacade.getFromUsername("user1");
this.currentUser.getPersonPhones().isEmpty();
} catch (Exception e) {
}
}
public Person getCurrentUser() {
return currentUser;
}
public void setCurrentUser(Person currentUser) {
this.currentUser = currentUser;
}
}
Now, the result we get is the one we expect only if we access the page right after having restarted the application server.
forename Alice
emails 2
phones {[sums.groupa.entities.Phone[id=38]]}
accountType Student
If we modify anything (except for the view) and save, after the inevitable deploy, the result is different.
forename Alice
emails 0
phones {[]}
accountType Student
Why is that happening and how can we avoid it?
Thanks in advance.
AG
A couple of contributors (that I want to thank for their quick replies) asked for the PersonFacade implementation.
public Person getFromUsername(String username)
{
try
{
Query q = em.createQuery("SELECT p FROM Person p LEFT JOIN FETCH p.personEmails WHERE UPPER(p.username) = :username");
q.setParameter("username", username.toUpperCase());
return (Person) q.getSingleResult();
}
catch (Exception ex)
{
Logger.getLogger(PersonFacade.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
}
As you can see I tried to use FETCH JOIN as suggested but the query is getting out too many results: it should fetch only one instance of Person representing Alice and containing two instances of Email in the personEmails field but I suspect it is getting two different instances of Person, each having a different instance of Email attached.
The original query was as follows:
SELECT p FROM Person p WHERE UPPER(p.username) = :username
Thanks again.
AG
I don't know how you wrote your personFacade and got the Person entity.
But I guess you used query.
If you are using JPQL, try fetch join.
Say I have two domain objects and a mapper interface.
class Person {
int id;
List<Problem> problems = new ArrayList<Problem>();
}
class Problem {
int id;
Person person;
}
interface PersonMapper {
public List<Person> selectAllPersons();
}
And two database tables.
create table person (
id integer not null generated always as identity constraint person_pk primary key,
)
create table problem (
id integer not null generated always as identity constraint problem_pk primary key,
person_id integer not null constraint problem_person_fk references person
)
I can create a mapping file that gets the data I want.
<resultMap id="personMap" type="Person">
<id column="person_id" property="id" />
<collection column="problem_person_id" property="problems"
javaType="ArrayList" ofType="Problem" resultMap="problemMap" />
</resultMap>
<resultMap id="problemMap" type="Problem">
<id column="problem_id" property="id" />
<!-- Adding an association here will cause a circular dependency -->
<!-- The circular dependency results in a StackOverflowException -->
</resultMap>
<select id="selectAllPersons" resultMap="personMap">
select
person.id as person_id,
problem.id as problem_id
from person left outer join problem on person.id = problem.person_id
</select>
However, since MyBatis doesn't do bi-directional mapping, none of the Problem objects in the returned collections will have their Person reference set correctly.
According to this issue, it sounds like I should be able to update my mapper interface and add a custom result handler that can be supplied by the calling class.
interface PersonMapper {
public List<Person> selectAllPersons(ResultHandler handler);
}
class PersonResultHandler implements ResultHandler {
#Override
public void handleResult(ResultContext context) {
System.out.println(context.getResultObject());
}
}
class PersonDAO {
// Get SqlSession sqlSession
sqlSession.getMapper(PersonMapper.class).selectAllPersons(new PersonResultHandler());
}
However, the handleResult method of my ResultHandler never gets called. I've seen this example, but the extra fluff class in there makes it very difficult to understand. Can anyone give me a simple example of using a custom ResultHandler with mapper interfaces? I'm using MyBatis 3.0.5+.
I've also read through the MyBatis mailing list and there are several suggestions of using caching and lazy loading to solve circular dependencies, but I can't find any examples of how to do it.
You should replace your method declaration to:
interface PersonMapper {
public void selectAllPersons(ResultHandler handler);
}
And populate List<Person> inside your PersonResultHandler
class PersonResultHandler implements ResultHandler {
List<Person> persons = new ArrayList<Person>();
#Override
public void handleResult(ResultContext context) {
Object result = context.getResultObject();
if (result instanceof Person) {
Person person = (Person) result;
for (Problem problem : person.getProblems()) {
problem.setPerson(person);
}
persons.add(person);
}
}
public List<Person> getPersons() {
return persons;
}
}