Postgres to bring list all of table fields for particular Employee row? - postgresql

Taking a reference from link: Postgres to fetch the list having comma separated values, I want to write a query which somhow brings Employee email Table fields as a list for a particular Empployee. This is needed for Spring Batch to Simply match it from the Resultset and create a POJO/Model class like List emails for Employee class?
Can this be possible ?
select c.*, ce.*, string_agg(ce.email, ',') as emails
from root.employee c
full outer join root.employee_email ce
on c.employee_id = ce.employee_id
group by
c.employee_id, ce.employee_email_id
order by
c.employee_id
limit 1000
offset 0;

Your problem is a common one in the batch processing realm and with Spring Batch it is called "Driving Query Based ItemReaders", you can find more about that in here.
Basically you retrieve the Contacts in your reader, and in your processor you add the list of Emails to them.
#Bean(destroyMethod = "")
public JdbcCursorItemReader<Employee> employeeReader(DataSource dataSource) {
JdbcCursorItemReader<Employee> ItemReader = new JdbcCursorItemReader<>();
ItemReader.setDataSource(dataSource);
ItemReader.setSql("SELECT * FROM employee.employee C ");
ItemReader.setRowMapper(new EmployeeRowMapper());
return ItemReader;
}
#Bean
public ItemProcessor<Employee, Employee> settlementHeaderProcessor(JdbcTemplate jdbcTemplate){
return item -> {
root.EMPLOYEE_EMAIL CE WHERE ce.employee_id = ? ",
new Object[]{item.getId()},
new RowMapper<String>() {
#Override
public String mapRow(ResultSet resultSet, int i) throws SQLException {
return resultSet.getString("EMAIL");
}
});
item.setEmails(emails);
return item;
};
}
PS : this could have some performance issues if you have lots of contacts, because for each contact Item you will hit the database to retrieve Emails.
There is another optimized way, by creating a custom reader that will return a List of Contacts (For example 1000 by 1000), and a processor that will enrich them with their emails. This way you will hit the database again for each 1000 Contact Item.
In your reader your retrieve a list of unique Employees page per page (Say your page is 1000 long).
And in your processor for the 1000 employees you retrieve all their emails in one query.
Then for each employee you set the emails retrieved in the last query.
An example might like the following:
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Integer> {
}
#Getter
class EmployeeVO {
private Long employeeId;
private String email;
EmployeeVO(Long employeeId, String email) {
this.employeeId= employeeId;
this.email = email;
}
}
public class EmployeeListReader implements ItemReader<List<Employee>> {
private final static int PAGE_SIZE = 1000;
private final EmployeeRepository employeeRepository;
private int page = 0;
public EmployeeListReader(EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
public List<Employee> read() throws Exception {
Page<Employee> employees = employeeRepository.findAll(PageRequest.of(page, PAGE_SIZE));
page++;
return employees.getContent();
}
}
#Bean
EmployeeListReader reader(){
return new EmployeeListReader(this.employeeRepository);
}
#Bean
public ItemProcessor<List<Employee>, List<Employee>> settlementHeaderProcessor(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
return item -> {
List<Long> employeesIds = item.stream().map(Employee::getId).collect(Collectors.toList());
SqlParameterSource parameters = new MapSqlParameterSource("ids", employeesIds);
List<ContactVO> emails = namedParameterJdbcTemplate
.query("SELECT CE.employeeId, CE.EMAIL FROM employee_EMAIL CE WHERE ce.contact_id IN (:ids) ",
parameters,
new RowMapper<ContactVO>() {
#Override
public ContactVO mapRow(ResultSet resultSet, int i) throws SQLException {
return new ContactVO(
resultSet.getLong("EMPLOYEE_ID"),
resultSet.getString("EMAIL"));
}
});
Map<Long, List<ContactVO>> emailsByContactId = emails.stream().collect(Collectors.groupingBy(ContactVO::getContactId));
List<Employee> newEmployeesWithEmails = Collections.unmodifiableList(item);
newEmployeesWithEmails.forEach(employee -> {
employee.setEmails(emailsByContactId.get(employee.getId()).stream().map(ContactVO::getEmail).collect(Collectors.toList()));
});
return newEmployeesWithEmails;
};
}
Hope this helps

Related

How is it possible to access pre-filtered dependent entities from Associations retrieved via JPA/Hibernate criteria query with restrictions?

I have a provider:user=1:N association modeled with entities, Hibernate/JPA.
Then I want to query a provider/user pair via restrictions on attributes of the dependent entity, like certain values for the attributes of the user, let’s say its id, date of birth, etc.
The logged sql has a proper join and all the attributes of the two entities in the select. I tried it manually, it returns the expected single row.
Thus, on entity level, I expect a single provider entity to be returned with the user list containing the queried user.
Indeed, the corresponding provider entity is returned, but when I then want to access the user via the provider’s user list, it hits the DB a second time and reads all users of the provider totally neglecting my restrictions of the query.
The observed behavior is the same for queries formulated with HQL, Hibernate Criteria (also with #Filter), JPA CriteriaBuilder.
What am I missing here?
Do those restrictions only affect the selection of the root entities (which is provider in my case)?
The problem is sketched on
https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/querycriteria.html
Under 5.4 Associations it says:
The kittens collections held by the Cat instances returned by the previous two queries are not pre-filtered by the criteria. If you want to retrieve just the kittens that match the criteria, you must use a ResultTransformer.
Is this thus the intended behavior for this kind of API?
Or is there a convenient possibility to access just the restricted sub set of the dependent entities?
Regards,
Wolfgang
Here comes some source code to precise my verbal description above.
Provider and User table.
drop table EX_USER;
drop table EX_PROVIDER;
create table EX_PROVIDER
( id number(*,0) not null
,name varchar2(255) not null
,primary key (id)
);
insert into EX_PROVIDER (id,name) values (0 ,'Provider_A');
insert into EX_PROVIDER (id,name) values (1 ,'Provider_B');
commit;
create table EX_USER
( id number(*,0) not null
, ex_provider_id number(*,0) not null
,name varchar2(255)
,location varchar2(255)
,primary key (id)
,constraint ex_user_provider_fk foreign key(ex_provider_id) references EX_PROVIDER(id)
);
insert into EX_USER (id,ex_provider_id,name,location) values (0,0,'User_1','Munich');
insert into EX_USER (id,ex_provider_id,name,location) values (1,0,'User_2','Berlin');
insert into EX_USER (id,ex_provider_id,name,location) values (2,1,'User_3','Munich');
commit;
Entities generated with Eclipse "JPA Tools".
#Entity
#Table(name="EX_PROVIDER")
#NamedQuery(name="ExProvider.findAll", query="SELECT e FROM ExProvider e")
public class ExProvider implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="EX_PROVIDER_ID_GENERATOR", sequenceName="KONST_SD_SEQ")
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="EX_PROVIDER_ID_GENERATOR")
private long id;
private String name;
//bi-directional many-to-one association to ExUser
#OneToMany(mappedBy="exProvider",fetch=FetchType.LAZY)
private Set<ExUser> exUsers=new HashSet<ExUser>();
public ExProvider() {
}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Set<ExUser> getExUsers() {
return this.exUsers;
}
public void setExUsers(Set<ExUser> exUsers) {
this.exUsers = exUsers;
}
public ExUser addExUser(ExUser exUser) {
getExUsers().add(exUser);
exUser.setExProvider(this);
return exUser;
}
public ExUser removeExUser(ExUser exUser) {
getExUsers().remove(exUser);
exUser.setExProvider(null);
return exUser;
}
}
#Entity
#Table(name="EX_USER")
#NamedQuery(name="ExUser.findAll", query="SELECT e FROM ExUser e")
public class ExUser implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="EX_USER_ID_GENERATOR", sequenceName="KONST_SD_SEQ")
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="EX_USER_ID_GENERATOR")
private long id;
private String location;
private String name;
//bi-directional many-to-one association to ExProvider
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="EX_PROVIDER_ID")
private ExProvider exProvider;
public ExUser() {
}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public String getLocation() {
return this.location;
}
public void setLocation(String location) {
this.location = location;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public ExProvider getExProvider() {
return this.exProvider;
}
public void setExProvider(ExProvider exProvider) {
this.exProvider = exProvider;
}
}
My intention with the below code was to retrieve Provider 0 (Provider_A) containing User 1 (Berlin) in its user list.
#SuppressWarnings("unchecked")
private void demo() {
EntityManager em = null;
try {
em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
Session session = (Session) em.getDelegate();
Criteria crit = session.createCriteria(ExProvider.class, "provider")
.createCriteria("exUsers", "user")
.add(Restrictions.eq("user.location","Berlin"))
;
List<ExProvider> providerList=(List<ExProvider>)crit.list();
logExProviderList(providerList);
tx.commit();
} finally {
if (tx!=null && tx.isActive()) tx.rollback();
}
} finally {
if (em!=null) em.close();
}
}
private void logExProviderList(List<ExProvider> providerList) {
for (ExProvider provider: providerList) {
logger.info("Criteria: provider=["+provider.getId()+"]");
for (ExUser user : provider.getExUsers()) {
logger.info("Criteria: user=["+user.getId()+"] name=["+user.getName()+"] location=["+user.getLocation()+"]");
}
}
}
The 1st SQL is what I expected. It is executed when crit.list() is called. On SQL level it returns the expected single row for Provider 0, User 1.
SELECT this_.id AS id43_1_,
this_.name AS name43_1_,
user1_.id AS id44_0_,
user1_.EX_PROVIDER_ID AS EX4_44_0_,
user1_.location AS location44_0_,
user1_.name AS name44_0_
FROM EX_PROVIDER this_
INNER JOIN EX_USER user1_
ON this_.id=user1_.EX_PROVIDER_ID
WHERE user1_.location=?;
However, this is not mapped to entity level as I expected. There, the restriction seems to affect the selection of the Provider only. When the user list is accessed, all users of the provider are read from DB neglecting the 'Berlin' restriction.
This was the same whether I used HQL, Hibernate Criteria (also with #Filter), JPA CriteriaBuilder.
SELECT exusers0_.EX_PROVIDER_ID AS EX4_43_1_,
exusers0_.id AS id1_,
exusers0_.id AS id44_0_,
exusers0_.EX_PROVIDER_ID AS EX4_44_0_,
exusers0_.location AS location44_0_,
exusers0_.name AS name44_0_
FROM EX_USER exusers0_
WHERE exusers0_.EX_PROVIDER_ID=?;
The log.
Criteria: provider=[0]
Criteria: user=[1] name=[User_2] location=[Berlin]
Criteria: user=[0] name=[User_1] location=[Munich]
I may achieve the expected result set using setResultTransformer(), but in this case the properly selected result is returned as some rows of entities.
#SuppressWarnings("unchecked")
private void demo() {
EntityManager em = null;
try {
em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
Session session = (Session) em.getDelegate();
Criteria crit = session.createCriteria(ExProvider.class, "provider")
.createCriteria("exUsers", "user")
.add(Restrictions.eq("user.location","Berlin"))
.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
;
List<ExProvider> providerList=(List<ExProvider>)crit.list();
logExProviderMapList(providerList);
tx.commit();
} finally {
if (tx!=null && tx.isActive()) tx.rollback();
}
} finally {
if (em!=null) em.close();
}
}
private void logExProviderMapList(List providerList) {
Iterator iter = providerList.iterator();
while ( iter.hasNext() ) {
Map map = (Map) iter.next();
ExProvider provider = (ExProvider) map.get("provider");
if(provider!=null) {
logger.info("Criteria: provider=["+provider.getId()+"]");
}
ExUser user = (ExUser) map.get("user");
if(user!=null) {
logger.info("Criteria: user=["+user.getId()+"] name=["+user.getName()+"] location=["+user.getLocation()+"]");
}
}
}
The SQL is the same as the 1st SQL above.
SELECT this_.id AS id43_1_,
this_.name AS name43_1_,
user1_.id AS id44_0_,
user1_.EX_PROVIDER_ID AS EX4_44_0_,
user1_.location AS location44_0_,
user1_.name AS name44_0_
FROM EX_PROVIDER this_
INNER JOIN EX_USER user1_
ON this_.id=user1_.EX_PROVIDER_ID
WHERE user1_.location=?;
The log.
Criteria: provider=[0]
Criteria: user=[1] name=[User_2] location=[Berlin]
My question was and is, whether it is possible to get this result as proper entity tree with Provider_A containing exactly the User from Berlin in its list.
Thanks again,
Wolfgang B

does find query return managed or detached entities in JTA transactions?

In the following class, both the methods return the same objects.
But is the list of objects returned from first is managed since it is part of transaction when compared to second one which is not part of transaction?
public class QueryServiceImpl implements QueryService {
#PersistenceContext(unitName="PC")
EntityManager em;
//default attribute
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public List findAlItems() {
return em.createQuery("SELECT item FROM Item item",
Item.class)
.getResultList();
}
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public List findAlItemsNoTransaction() {
return em.createQuery("SELECT item FROM Item item",
Item.class)
.getResultList();
}
Regarding your example, you're right. All entities should not be managed after returning from the second method (findAlItemsNoTransaction). However, if you would like to keep them managed you should use:
#PersistenceContext(type=EXTENDED)
like it was showed in JPA specification:
/*
* An extended transaction context is used. The entities remain
* managed in the persistence context across multiple transactions.
*/
#Stateful
#Transaction(REQUIRES_NEW)
public class ShoppingCartImpl implements ShoppingCart {
#PersistenceContext(type=EXTENDED)
EntityManager em;
private Order order;
private Product product;
public void initOrder(Long id) {
order = em.find(Order.class, id);
}
public void initProduct(String name) {
product = (Product) em.createQuery("select p from Product p
where p.name = :name")
.setParameter("name", name)
.getSingleResult();
}
public LineItem createLineItem(int quantity) {
LineItem li = new LineItem(order, product, quantity);
order.getLineItems().add(li);
em.persist(li);
return li;
}
}

JPA query returning duplicated values

I have a jpa query that should return 4 rows, and it does return 4 rows, but only the first 2 rows of the result are ok, the next 2 are the first row repeated. And no, the first row is not repeated in the database.
this is my query:
public static List<DetalleProcesosEntity> getALLbyid(int id_proceso){
Query q = entityManager.createQuery("select a from DetalleProcesosEntity a where a.idProceso=:id_proceso");
q.setParameter("id_proceso", id_proceso);
List<DetalleProcesosEntity>resultado=q.getResultList();
List<DetalleProcesosEntity>result=new ArrayList<DetalleProcesosEntity>(resultado);
return result;
}
what im doing is:
from a view called "procesos" wich contains a table i click in any of the "procesos" in the table and i go to its details (passing the "proceso" ID as a parameter):
this is the details backing bean:
#ManagedBean(name = "det_procesos")
#ViewScoped
public class DetalleProcesosBean {
private int idProceso;
private List<DetalleProcesosEntity>detalles;
#PostConstruct
public void init(){
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
//Obtener parametros del request
Map<String, String> parameterMap = externalContext.getRequestParameterMap();
idProceso = Integer.parseInt(parameterMap.get("id_proc"));
detalles= Procesos.getALLbyid(idProceso);
}
public List<DetalleProcesosEntity> getDetalles() {
return detalles;
}
public void setDetalles(List<DetalleProcesosEntity> detalles) {
this.detalles = detalles;
}
public int getIdProceso() {
return idProceso;
}
public void setIdProceso(int idProceso) {
this.idProceso = idProceso;
}
}
if i use distinct in the query it does not make any difference, i just get 2 rows and im sure in the database there are 4 rows with the same "id proceso" and different values.
I solved it.. it was because i was working with views and i did not setted a proper ID to one of them (i had to add #Id to one of attributes of the entity)

EclipseLink native query result into POJO - Missing descriptor for [Class]

I'm using EclipseLink to run some Native SQL. I need to return the data into a POJO. I followed the instructions at EclipseLink Docs, but I receive the error Missing descriptor for [Class]
The query columns have been named to match the member variables of the POJO. Do I need to do some additional mapping?
POJO:
public class AnnouncementRecipientsFlattenedDTO {
private BigDecimal announcementId;
private String recipientAddress;
private String type;
public AnnouncementRecipientsFlattenedDTO() {
super();
}
public AnnouncementRecipientsFlattenedDTO(BigDecimal announcementId, String recipientAddress, String type) {
super();
this.announcementId = announcementId;
this.recipientAddress = recipientAddress;
this.type = type;
}
... Getters/Setters
Entity Manager call:
public List<AnnouncementRecipientsFlattenedDTO> getNormalizedRecipientsForAnnouncement(int announcementId) {
Query query = em.createNamedQuery(AnnouncementDeliveryLog.FIND_NORMALIZED_RECIPIENTS_FOR_ANNOUNCEMENT, AnnouncementRecipientsFlattenedDTO.class);
query.setParameter(1, announcementId);
return query.getResultList();
}
I found out you can put the results of a Native Query execution into a List of Arrays that hold Objects. Then one can iterate over the list and Array elements and build the desired Entity objects.
List<Object[]> rawResultList;
Query query =
em.createNamedQuery(AnnouncementDeliveryLog.FIND_NORMALIZED_RECIPIENTS_FOR_ANNOUNCEMENT);
rawResultList = query.getResultList();
for (Object[] resultElement : rawResultList) {
AnnouncementDeliveryLog adl = new AnnouncementDeliveryLog(getAnnouncementById(announcementId), (String)resultElement[1], (String)resultElement[2], "TO_SEND");
persistAnnouncementDeliveryLog(adl);
}
You can only use native SQL queries with a class if the class is mapped. You need to define the AnnouncementRecipientsFlattenedDTO class as an #Entity.
Otherwise just create the native query with only the SQL and get an array of the data back and construct your DTO yourself using the data.
Old question but may be following solution will help someone else.
Suppose you want to return a list of columns, data type and data length for a given table in Oracle. I have written below a native sample query for this:
private static final String TABLE_COLUMNS = "select utc.COLUMN_NAME, utc.DATA_TYPE, utc.DATA_LENGTH "
+ "from user_tab_columns utc "
+ "where utc.table_name = ? "
+ "order by utc.column_name asc";
Now the requirement is to construct a list of POJO from the result of above query.
Define TableColumn entity class as below:
#Entity
public class TableColumn implements Serializable {
#Id
#Column(name = "COLUMN_NAME")
private String columnName;
#Column(name = "DATA_TYPE")
private String dataType;
#Column(name = "DATA_LENGTH")
private int dataLength;
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public int getDataLength() {
return dataLength;
}
public void setDataLength(int dataLength) {
this.dataLength = dataLength;
}
public TableColumn(String columnName, String dataType, int dataLength) {
this.columnName = columnName;
this.dataType = dataType;
this.dataLength = dataLength;
}
public TableColumn(String columnName) {
this.columnName = columnName;
}
public TableColumn() {
}
#Override
public int hashCode() {
int hash = 0;
hash += (columnName != null ? columnName.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
if (!(object instanceof TableColumn)) {
return false;
}
TableColumn other = (TableColumn) object;
if ((this.columnName == null && other.columnName != null) || (this.columnName != null && !this.columnName.equals(other.columnName))) {
return false;
}
return true;
}
#Override
public String toString() {
return getColumnName();
}
}
Now we are ready to construct a list of POJO. Use the sample code below to construct get your result as List of POJOs.
public List<TableColumn> findTableColumns(String table) {
List<TableColumn> listTables = new ArrayList<>();
EntityManager em = emf.createEntityManager();
Query q = em.createNativeQuery(TABLE_COLUMNS, TableColumn.class).setParameter(1, table);
listTables = q.getResultList();
em.close();
return listTables;
}
Also, don't forget to add in your POJO class in persistence.xml! It can be easy to overlook if you are used to your IDE managing that file for you.
Had the same kind of problem where I wanted to return a List of POJOs, and really just POJOs (call it DTO if you want) and not #Entity annotated Objects.
class PojoExample {
String name;
#Enumerated(EnumType.STRING)
SomeEnum type;
public PojoExample(String name, SomeEnum type) {
this.name = name;
this.type = type;
}
}
With the following Query:
String query = "SELECT b.name, a.newtype as type FROM tablea a, tableb b where a.tableb_id = b_id";
Query query = getEntityManager().createNativeQuery(query, "PojoExample");
#SuppressWarnings("unchecked")
List<PojoExample> data = query.getResultList();
Creates the PojoExample from the database without the need for an Entity annotation on PojoExample. You can find the method call in the Oracle Docs here.
edit:
As it turns out you have to use #SqlResultSetMapping for this to work, otherwise your query.getResultList() returns a List of Object.
#SqlResultSetMapping(name = "PojoExample",
classes = #ConstructorResult(columns = {
#ColumnResult(name = "name", type = String.class),
#ColumnResult(name = "type", type = String.class)
},
targetClass = PojoExample.class)
)
Just put this anywhere under your #Entity annotation (so in this example either in tablea or tableb because PojoExample has no #Entity annotation)

How to edit value of ValueProxy of gwt requestfactory at client side?

I have 2 models: ContactGroup and Contact. ContactGroup contains many Contacts.
In the page, I have to display a list of groups and number of contacts in the correspondence group like this:
Group Foo (12 contacts)
Group Bar (20 contacts)
So I at server side I used a DTO ContactGroupInfo:
public class ContactGroupInfo {
private Integer contactCount;
private Long id;
private String name;
public Integer getContactCount() { return this.contactCount; }
public Long getId() { return this.id; }
public String getName() { return this.name; }
public void setContactCount(Integer count) { this.contactCount = count; }
public void setId(Long id) { this.id = id; }
public void setName(String name) { this.name = name; }
}
In this ContactGroupInfo, I added contactCount field which is not a field in ContactGroup entity.
And at client side, I used a ValueProxy:
#ProxyFor(value = ContactGroupInfo.class, locator = ContactGroupService.class)
public interface LightContactGroupProxy extends ValueProxy {
Integer getContactCount();
Long getId();
String getName();
void setContactCount(Integer count);
void setId(Long id);
void setName(String name);
}
So when server side returns to client side a list of LightContactGroupProxy, I stored that list a in ArrayList to render to a CellTable.
And here is the problem comes to me: when I need to edit the name of the group at client side, I can't edit the LightContactGroupProxy object directly.
So I have to send the new name to server to return a new LightContactGroupProxy with the new name. This is not effective because I have to count contacts again (althought I know the number of contacts does not change).
Or I have to send both the number of contacts and new name to server to create a new LightContactGroupProxy with the new name. This is not I want, because if LightContactGroupProxy has many other fields I have to send many fields.
I don't know why GWT teams designs the immutable proxy. So please, someone has experience on requestfactory please show me the correct way to handle ValueProxy returned from server so that we can use them to render and edit?
Thank you
Maybe you should try something like this :
ContactGroupContext ctx = requestFactory.newContactGroupContext();
LightContactGroupProxy editableProxy = ctx.edit(lightContactGroupProxy);
editableProxy.setName(newName);
ctx.saveInfoAndReturn(editableProxy).fire(receiver); // or just ctx.fire();
Anyway, I wouldn't use ValueProxy in this case, I would directly get the ContactGroup entities with a transiant property contactCount. The property could be a primitive, or a ValueProxy if you don't want it to be calculated every time a ContactGroup is requested.