I have an entity named certificate
#Entity
public class CertificateData {
#Id private String id;
private long expireDate;
private long createDate;
private int status;
private String subjectDN;
...
}
Many certificate can have the same subject and I want to select all certificates that have count of the field subjectId different from 3. I used this code and it worked
public List<CertificateData> findAllByRedundancy() {
Map<String, Integer> subjectDNCount = new HashMap<>();
Map<String, List<CertificateData>> subjectDNCertificates = new HashMap<>();
List<CertificateDto> certificateDataList =
this.certificaterepository
.findAll().forEach(certificateData -> {
String subject = certificateData.getSubjectDN();
if(subjectDNCount.containsKey(subject)){
subjectDNCount.put(subject, subjectDNCount.get(subject)+1);
subjectDNCertificates.get(subject).add(certificateData);
}
else{
subjectDNCount.put(subject, 1);
subjectDNCertificates.put(subject, new ArrayList<>());
subjectDNCertificates.get(subject).add(certificateData);
}
});
List<CertificateDto> result = new ArrayList<>();
subjectDNCount.forEach((s, i) -> {
if(i!=3){
result.addAll(subjectDNCertificates.get(s));
}
});
return result;
}
I tried to do the same thing using a Query-annotated Spring JPA which looks like this:
#Query("select c from CertificateData c group by c.subjectDN Having count(c) <> 3")
List<CertificateData> findAllByRedundancy();
But it doesn't return all the certificates. It returns only certificates by distincts subjectDN.
It looks like the query in your annotation is selecting groups instead of the CertificateData records. One way to get the CertificateData records themselves would be to use a sub-query to find the desired subjectDN values (which is what you already have), and then have the outer query find the records with those subjectDN values. I haven't tested this yet, but in JPQL it would be something like:
#Query(
"select c from CertificateData c where c.subjectDN in " +
"(" +
" select c.subjectDN from CertificateData c " +
" group by c.subjectDN having count(c) <> 3" +
")"
)
List<CertificateData> findAllByRedundancy();
For reference, the SQL (specifically postgres) to do this would be something like:
select * from certificate_data where subject_dn in
(
select subject_dn from certificate_data
group by subject_dn having count(*) <> 3
)
I have faced to problem. I have two tables t1 and t2. And they connected with foreign key(more exactly t1 has field id and t2 table has id_t1_fkey field). One record in t1 can have one or more records in t2. So I want to put all of it in list of object using dto constructor. And as a result I wish to get such kind of result:
And object number i in list:
Object_i: field1_t1, field2_t2, fieldn_t1, List[field1_t2]
Ivanov Ivan [order1,order2, order3]
Now hql returns only objects such kind:
Ivanov Ivan order1
Ivanov Ivan order2
Ivanov Ivan order3
So you can see three useless copies Ivanov Ivan((( I don't want to send frontend a lot of copies
I have tried to write this:
t1 is table SettingsFee,which was connected #OneToMany with t2
t2 is "table" s.salesPartner,
//t1 Model
#Entity
#Table (name="t1")
#Audited
public class SettingsFee {
public static enum Status {
ACTIVE,
CLOSED,
DELETE
}
#Id
#Column(name="id")
private String id;
#Column(name="name")
private String name;
#Column(name="lastname")
private String lastname;
#OneToMany(mappedBy = "settingsFee")
private List<SettingsFeeSalesPartner> salesPartner;
//there getters and setters
}
//t2 Model
#Entity
#Table(name = "t2")
#Audited
public class SettingsFeeSalesPartner {
#Id
#Column(name = "id")
private String id;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "settingsfee")
private SettingsFee settingsFee;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "salespartner")
private Company salesPartner;//Company is another model for another table
//there getters and setters
}
#Repository
public class SettingsFeeCustomRepository {
#PersistenceContext
private EntityManager em;
public List<SettingsFeeDTO> loadData() {
//there I try to choose that id which satisfy a query using filters,
//number of results etc, but there they were deleted for simplicity.
String hql = "SELECT DISTINCT(s.id) FROM SettingsFee s INNER JOIN s.salesPartner psp WHERE s.status != 'DELETE' ";
Query PreQuery = em.createQuery(hql)
//there I try to take a result list with necessary fields:
String hql1 = "SELECT sf.id, sf.name sf.lastname, sfp.salesPartner.id,, sfp.salesPartner.name "
+ " FROM SettingsFee sf "
+ "JOIN sf.salesPartner sfp "
+ "WHERE sf.id IN ( :PreQuery ) "
+ "ORDER BY sf.id";
Query query = em.createQuery(hql1);
List<String> preresult = PreQuery.getResultList();
if (preresult.isEmpty()) {
query.setParameter("PreQuery", "_");
} else {
query.setParameter("PreQuery", preresult);
}
//there I make an Array to List
List<SettingsFeeLiteHQLDTO>list= ((List<Object[]>) query.getResultList()).stream()
.map(row -> new SettingsFeeLiteHQLDTO((String) row[0], (String) row[1], (String) row[2], (String) row[3]))
.collect(Collectors.toList());
//there I pack my list to "a list with list":
//if id is equal, it will be replace in list.
List<SettingsFeeDTO> FinList= new ArrayList<SettingsFeeDTO>();
List<String> spName=new ArrayList<String>();
int NumList=list.size();
for (int i=0;i<NumList;i++){
spName.add(list.get(i).getClientName() );
spId.add(list.get(i).getClientId());
//there equals was Overrided and two objects are equal if their id identical
if ((i!=NumList-1)&&list.get(i).equals(list.get(i+1))){
//do nothing
}else if((i!=NumList-1)&&(list.get(i).equals(list.get(i+1))==false)){ //if two nabes are different
//there is special constructor
FinList.add(new SettingsFeeDTO(list.get(i),spId,spName));
spName=new ArrayList<String>();
spId=new ArrayList<String>();
}
else{//for last element
FinList.add(new SettingsFeeDTO(list.get(i),spId,spName));
}
}
return FinList;
}
How to overcome??? Is there any variants to minimize the amount of code?
P.S. I use Eclipse, spring framework, data:pgAdmin(postgre)
Following up on a question I posted yesterday: How to populate POJO class from custom Hibernate query?
Can someone show me an example of how to code the following SQL in Hibernate, and get the results correctly?
SQL:
select firstName, lastName
from Employee
What I'd like to do, if it's possible in Hibernate, is to put the results in their own base class:
class Results {
private firstName;
private lastName;
// getters and setters
}
I believe it's possible in JPA (using EntityManager), but I haven't figured out how to do it in Hibernate (using SessionFactory and Session).
I'm trying to learn Hibernate better, and even this "simple" query is proving confusing to know what form Hibernate returns the results, and how to map the results into my own (base) class. So at the end of the DAO routine, I'd do:
List<Results> list = query.list();
returning a List of Results (my base class).
select firstName, lastName from Employee
query.setResultTransformer(Transformers.aliasToBean(MyResults.class));
You can't use above code with Hibernate 5 and Hibernate 4 (at least Hibernate 4.3.6.Final), because of an exception
java.lang.ClassCastException: com.github.fluent.hibernate.request.persistent.UserDto cannot be cast to java.util.Map
at org.hibernate.property.access.internal.PropertyAccessMapImpl$SetterImpl.set(PropertyAccessMapImpl.java:102)
The problem is that Hibernate converts aliases for column names to upper case — firstName becomes FIRSTNAME. And it try to find a getter with name getFIRSTNAME(), and setter setFIRSTNAME() in the DTO using such strategies
PropertyAccessStrategyChainedImpl propertyAccessStrategy = new PropertyAccessStrategyChainedImpl(
PropertyAccessStrategyBasicImpl.INSTANCE,
PropertyAccessStrategyFieldImpl.INSTANCE,
PropertyAccessStrategyMapImpl.INSTANCE
);
Only PropertyAccessStrategyMapImpl.INSTANCE suits, in opinion of Hibernate, well. So after that it tries to do conversion (Map)MyResults.
public void set(Object target, Object value, SessionFactoryImplementor factory) {
( (Map) target ).put( propertyName, value );
}
Don't know, it is a bug or feature.
How to solve
Using aliases with quotes
public class Results {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
String sql = "select firstName as \"firstName\",
lastName as \"lastName\" from Employee";
List<Results> employees = session.createSQLQuery(sql).setResultTransformer(
Transformers.aliasToBean(Results.class)).list();
Using a custom result transformer
Another way to solve the problem — using a result transformer that ignores method names case (treat getFirstName() as getFIRSTNAME()). You can write your own or use FluentHibernateResultTransformer. You will not need to use quotes and aliases (if you have column names equal to DTO names).
Just download the library from the project page (it doesn't need additional jars): fluent-hibernate.
String sql = "select firstName, lastName from Employee";
List<Results> employees = session.createSQLQuery(sql)
.setResultTransformer(new FluentHibernateResultTransformer(Results.class))
.list();
This transformer can be used for nested projections too: How to transform a flat result set using Hibernate
See AliasToBeanResultTransformer:
Result transformer that allows to transform a result to a user specified class which will be populated via setter methods or fields matching the alias names.
List resultWithAliasedBean = s.createCriteria(Enrolment.class)
.createAlias("student", "st")
.createAlias("course", "co")
.setProjection( Projections.projectionList()
.add( Projections.property("co.description"), "courseDescription" )
)
.setResultTransformer( new AliasToBeanResultTransformer(StudentDTO.class) )
.list();
StudentDTO dto = (StudentDTO)resultWithAliasedBean.get(0);
Your modified code:
List resultWithAliasedBean = s.createCriteria(Employee.class, "e")
.setProjection(Projections.projectionList()
.add(Projections.property("e.firstName"), "firstName")
.add(Projections.property("e.lastName"), "lastName")
)
.setResultTransformer(new AliasToBeanResultTransformer(Results.class))
.list();
Results dto = (Results) resultWithAliasedBean.get(0);
For native SQL queries see Hibernate documentation:
13.1.5. Returning non-managed entities
It is possible to apply a ResultTransformer to native SQL queries, allowing it to return non-managed entities.
sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
.setResultTransformer(Transformers.aliasToBean(CatDTO.class))
This query specified:
the SQL query string
a result transformer
The above query will return a list of CatDTO which has been instantiated and injected the values of NAME and BIRTHNAME into its corresponding properties or fields.
You need to use a constructor and in the hql use new. I let you the code example taken from this question: hibernate HQL createQuery() list() type cast to model directly
class Result {
private firstName;
private lastName;
public Result (String firstName, String lastName){
this.firstName = firstName;
this.lastName = lastName;
}
}
then your hql
select new com.yourpackage.Result(employee.firstName,employee.lastName)
from Employee
and your java (using Hibernate)
List<Result> results = session.createQuery("select new com.yourpackage.Result(employee.firstName,employee.lastName) from Employee").list();
YMMV but I've found that the key factor is you must make sure to alias every field in your SELECT clause with the SQL "AS" keyword. I've never had to use quotes around the alias names. Also, in your SELECT clause use the case and punctuation of the actual columns in your database and in the aliases use the case of the fields in your POJO. This has worked for me in Hibernate 4 and 5.
#Autowired
private SessionFactory sessionFactory;
...
String sqlQuery = "SELECT firstName AS firstName," +
"lastName AS lastName from Employee";
List<Results> employeeList = sessionFactory
.getCurrentSession()
.createSQLQuery(sqlQuery)
.setResultTransformer(Transformers.aliasToBean(Results.class))
.list();
If you have multiple tables you can use table aliases in the SQL as well. This contrived example with an additional table named "Department" uses more traditional lower case and underscores in database field names with camel case in the POJO field names.
String sqlQuery = "SELECT e.first_name AS firstName, " +
"e.last_name AS lastName, d.name as departmentName" +
"from Employee e, Department d" +
"WHERE e.department_id - d.id";
List<Results> employeeList = sessionFactory
.getCurrentSession()
.createSQLQuery(sqlQuery)
.setResultTransformer(Transformers.aliasToBean(Results.class))
.list();
java.lang.ClassCastException: "CustomClass" cannot be cast to java.util.Map.
This issue appears when the columns specified in SQL Query doesn't match with the columns of the mapping class.
It may be due to:
Non-matching casing of column name or
The column names are not matching or
column exist in query but missing in class.
JPQL case from hibernate 5.4:
Query<Employee> queryList = session.createQuery("select new xxx.xxx.Employee(e.firstName,e.lastName) from Employee e", Employee.class);
List<Employee> list = queryList.list();
Query<Long> queryCount = session.createQuery("select count(*) from Employee", Long.class);
Long count = queryCount.getSingleResult();
The select statement in JPQL is exactly the same as for HQL except that JPQL requires a select_clause, whereas HQL does not.
setResultTransformer #Deprecated It should not be used
more on Hibernate_User_Guide.html#hql-select
In case you have a native query, all answers here use deprecated methods for newer versions of Hibernate, so if you are using 5.1+ this is the way to go:
// Note this is a org.hibernate.query.NativeQuery NOT Query.
NativeQuery query = getCurrentSession()
.createNativeQuery(
"SELECT {y.*} , {x.*} from TableY y left join TableX x on x.id = y.id");
// This maps the results to entities.
query.addEntity("x", TableXEntity.class);
query.addEntity("y", TableYEntity.class);
query.list()
Below is a result transformer that ignores case:
package org.apec.abtc.dao.hibernate.transform;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyChainedImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyFieldImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyMapImpl;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.transform.AliasedTupleSubsetResultTransformer;
/**
* IgnoreCaseAlias to BeanResult Transformer
*
* #author Stephen Gray
*/
public class IgnoreCaseAliasToBeanResultTransformer extends AliasedTupleSubsetResultTransformer
{
/** The serialVersionUID field. */
private static final long serialVersionUID = -3779317531110592988L;
/** The resultClass field. */
#SuppressWarnings("rawtypes")
private final Class resultClass;
/** The setters field. */
private Setter[] setters;
/** The fields field. */
private Field[] fields;
private String[] aliases;
/**
* #param resultClass
*/
#SuppressWarnings("rawtypes")
public IgnoreCaseAliasToBeanResultTransformer(final Class resultClass)
{
if (resultClass == null)
{
throw new IllegalArgumentException("resultClass cannot be null");
}
this.resultClass = resultClass;
this.fields = this.resultClass.getDeclaredFields();
}
#Override
public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
return false;
}
/**
* {#inheritDoc}
*/
#Override
public Object transformTuple(final Object[] tuple, final String[] aliases)
{
Object result;
try
{
if (this.setters == null)
{
this.aliases = aliases;
setSetters(aliases);
}
result = this.resultClass.newInstance();
for (int i = 0; i < aliases.length; i++)
{
if (this.setters[i] != null)
{
this.setters[i].set(result, tuple[i], null);
}
}
}
catch (final InstantiationException | IllegalAccessException e)
{
throw new HibernateException("Could not instantiate resultclass: " + this.resultClass.getName(), e);
}
return result;
}
private void setSetters(final String[] aliases)
{
PropertyAccessStrategyChainedImpl propertyAccessStrategy = new PropertyAccessStrategyChainedImpl(
PropertyAccessStrategyBasicImpl.INSTANCE,
PropertyAccessStrategyFieldImpl.INSTANCE,
PropertyAccessStrategyMapImpl.INSTANCE
);
this.setters = new Setter[aliases.length];
for (int i = 0; i < aliases.length; i++)
{
String alias = aliases[i];
if (alias != null)
{
for (final Field field : this.fields)
{
final String fieldName = field.getName();
if (fieldName.equalsIgnoreCase(alias))
{
alias = fieldName;
break;
}
}
setters[i] = propertyAccessStrategy.buildPropertyAccess( resultClass, alias ).getSetter();
}
}
}
/**
* {#inheritDoc}
*/
#Override
#SuppressWarnings("rawtypes")
public List transformList(final List collection)
{
return collection;
}
#Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
IgnoreCaseAliasToBeanResultTransformer that = ( IgnoreCaseAliasToBeanResultTransformer ) o;
if ( ! resultClass.equals( that.resultClass ) ) {
return false;
}
if ( ! Arrays.equals( aliases, that.aliases ) ) {
return false;
}
return true;
}
#Override
public int hashCode() {
int result = resultClass.hashCode();
result = 31 * result + ( aliases != null ? Arrays.hashCode( aliases ) : 0 );
return result;
}
}
Writing (exist this type of Challenges working with hibernate)
Custom Queries
Custom Queries with Optional Parameters
Mapping Hibernate Custom query results to Custom class.
I am not saying about custom EntityRepository interface which extends JpaRepository on SpringBoot which you can write custom Query with #Query -> here you can't write query with optional params
e.g. if param is null don't append it in query string. And you can use Criteria api of hibernate but it not recommended in their documentation because of performance issue...
But exist simple and error prone and performance good way...
Write your own QueryService class which are methods will get
string(answer for first and second problem) sql and will map result to
Custom class (third problem) with it's any association #OneToMany, #ManyToOne ....
#Service
#Transactional
public class HibernateQueryService {
private final Logger log = LoggerFactory.getLogger(HibernateQueryService.class);
private JpaContext jpaContext;
public HibernateQueryService(JpaContext jpaContext) {
this.jpaContext = jpaContext;
}
public List executeJPANativeQuery(String sql, Class entity){
log.debug("JPANativeQuery executing: "+sql);
EntityManager entityManager = jpaContext.getEntityManagerByManagedType(Article.class);
return entityManager.createNativeQuery(sql, entity).getResultList();
}
/**
* as annotation #Query -> we can construct here hibernate dialect
* supported query and fetch any type of data
* with any association #OneToMany and #ManyToOne.....
*/
public List executeHibernateQuery(String sql, Class entity){
log.debug("HibernateNativeQuery executing: "+sql);
Session session = jpaContext.getEntityManagerByManagedType(Article.class).unwrap(Session.class);
return session.createQuery(sql, entity).getResultList();
}
public <T> List<T> executeGenericHibernateQuery(String sql, Class<T> entity){
log.debug("HibernateNativeQuery executing: "+sql);
Session session = jpaContext.getEntityManagerByManagedType(Article.class).unwrap(Session.class);
return session.createQuery(sql, entity).getResultList();
}
}
Use case - you can write any type condition about query params
#Transactional(readOnly = true)
public List<ArticleDTO> findWithHibernateWay(SearchFiltersVM filter){
Long[] stores = filter.getStores();
Long[] categories = filter.getCategories();
Long[] brands = filter.getBrands();
Long[] articles = filter.getArticles();
Long[] colors = filter.getColors();
String query = "select article from Article article " +
"left join fetch article.attributeOptions " +
"left join fetch article.brand " +
"left join fetch article.stocks stock " +
"left join fetch stock.color " +
"left join fetch stock.images ";
boolean isFirst = true;
if(!isArrayEmptyOrNull(stores)){
query += isFirst ? "where " : "and ";
query += "stock.store.id in ("+ Arrays.stream(stores).map(store -> store.toString()).collect(Collectors.joining(", "))+") ";
isFirst = false;
}
if(!isArrayEmptyOrNull(brands)){
query += isFirst ? "where " : "and ";
query += "article.brand.id in ("+ Arrays.stream(brands).map(store -> store.toString()).collect(Collectors.joining(", "))+") ";
isFirst = false;
}
if(!isArrayEmptyOrNull(articles)){
query += isFirst ? "where " : "and ";
query += "article.id in ("+ Arrays.stream(articles).map(store -> store.toString()).collect(Collectors.joining(", "))+") ";
isFirst = false;
}
if(!isArrayEmptyOrNull(colors)){
query += isFirst ? "where " : "and ";
query += "stock.color.id in ("+ Arrays.stream(colors).map(store -> store.toString()).collect(Collectors.joining(", "))+") ";
}
List<Article> articles = hibernateQueryService.executeHibernateQuery(query, Article.class);
/**
* MapStruct [http://mapstruct.org/][1]
*/
return articles.stream().map(articleMapper::toDto).collect(Collectors.toList());
}
I get a ClassCastException when trying to query my JPA entity class. I only want json to show two columns. That is name and address. How do I only show selected columns in JPA? From debugging it has to be the for loop. So List is the object and I need to make the right side an object instead of a list correct?
Entity
#Entity
#Table(name = "Personnel")
public class User implements Serializable {
private String id;
private String name;
private String address;
public User(String id, String name, String address)
{
this.id = id;
this.name = name;
this.address = address;
}
#Id
#Column(name = "name", unique = true, nullable = false)
public String getName() {
return this.name;
}....
//setters getters
Query/Impl
public List<User> getRecords(User ent){
String sql = "select "
+ " usr.name, usr.address "
+ " from User usr"
+ " where usr.id = '1' ";
List<User> records = this.getSession().createQuery(sql).list();
for ( User event : records ) {
System.out.println( "Records (" + event.getName + ") );
}
return records;
}
Update
This is my attempt to declare the result object as List. Does the method have to be an object instead of ?
public List<User> getRecords(User ent){
String sql = "select "
+ " usr.name, usr.address "
+ " from User usr"
+ " where usr.id = '1' ";
Map<String, String> results = new HashMap<String, String>();
List<Object[]> resultList = this.getSession().createQuery(sql).list();
// Place results in map
for (Object[] items: resultList) {
results.put((String)items[0], (String)items[1]);
results.toString();
}
return (List<User>) results;
You can pass a DTO class to the SELECT clause like this:
List<UserDTO> resultList = this.getSession().createQuery("""
select
new my.package.UserDTO(usr.name, usr.address)
from User usr
where usr.id = :userId
""")
.setParameter("userId", userId)
.getResultList();
You should read the complete object:
String sql = " from User usr"
+ " where usr.id = '1' ";
Or declare the result object as List<Object[]>
You can use generic(I assure you are using java 1.5 and above) and and one you get result, you do type check and downcast to desired type.
If You don't want to read the complete object you have to use Criteria and Projection on specific properties.
See this answer it will help
I have such a two classes:
public class Average {
long id;
String name;
double average;
public Average(long id , String name , double payment)
{
this.id = id;
this.name = name;
this.payment = payment;
}
#Override
public String toString()
{
return id + "\t" + name + " " + payment;
}
}
and
#Entity
public class Payment{
#Id
#GeneratedValue
long id;
Student student;
Subject subject;
double payment;
public Payment()
{
}
and I want to perform query on this class with java, but it is not working correctly. What can be wrong. Bellow I posted my query from another class where I call it:
public List<Object> getPayment()
{
Query q = entityManager.createQuery("Select NEW Average( g.subject.id , g.subject.name , AVG(g.payment) ) from Payment g GROUP BY g.subject.id, gge.subject.name");
return q.getResultList();
}
Please be patient with me, this is my first post!
Average is not an entity (you should annotate it with #Entity - like the one you did for Payment) so you cannot perform entityManager.createQuery().
Also you should specify table #Table(name = "XXX") after #Entity and before class name.
Query should be something like:
Select g.subject.id , g.subject.name , AVG(g.payment) from Average a, Payment g GROUP BY g.subject.id, g.subject.name
-- UPDATE
If Average is a Bean class used for projection then do the following:
Object[] payments = (Object[]) entityManager.createQuery("Select g.subject.id,
g.subject.name, AVG(g.payment) from Payment g
GROUP BY g.subject.id, g.subject.name").getSingleResult();
then iterative through the objects of payments:
for (Object object : payments) {
System.out.println(object);
}
if the result is just one row then put getSingleResult otherwise you need a List of the objects and iterate through the list:
List<Object[]> payments = (List<Object[]>) entityManager.createQuery("Select g.subject.id,
g.subject.name, AVG(g.payment) from Payment g
GROUP BY g.subject.id, g.subject.name").getResultList();
then iterative through the objects of payments:
if (payments != null){
for (Object[] object : payments) {
System.out.println(object[0] + " " + object[1] + " " + object[2]);
}
}
Could it be the gge instead of g in GROUP BY g.subject.id, gge.subject.name?