how to select multiple objects in spring data jpa using specifications - spring-data-jpa

I have following #Query, that is perfectly working fine. But now I have a scenario where there screen needs a filter, that will add some where clauses to the query.
#Query
("
SELECT
ef, ed, ea
FROM EntityA ea
JOIN EntityB eb
JOIN EntityC ec
JOIN EntityD ed
JOIN EntityE ee
JOIN EntityF ef
WHERE
TRUNC(ee.date) = TRUNC(:date)
-- conditions based on screen filter parameters
AND ef.amount = :amount
AND LOWER(ec.name) LIKE LOWER('%' || :name || '%')
AND ec.projectId = :projectId
AND ed.divisionId = :divisionId
")
Found that there is a good Specifications support to dynamically create the queries as per requirement.
But not sure how do I select multiple objects ef, ed & ea in one go using Specifications, otherwise I have to write 4 more queries to return result based on filter criteria.
N.B. Not using eager loading for performance reasons as entities are used by multiple services.

Specifications are only used for dynamically creating a where clause.
If you also need to control the select clause I recommend using the JPA Criteria API inside a custom method of your repository.

I was able to achieve this by implementing Custom Repositories, auto-wiring the EntityManager in that implementation class and then building the final JPQL based on parameters passed. A good example is in Eugen's blog.
Previously I had following structure
public interface EntityARepository extends JpaRepository<EntityA, Long> {
#Query(...)
List<EntityA> findAllBy(Date filterDate, Long amount, String name, Long projectId, Long divisionId);
}
public interface EntityAService {
List<Object[]> findAllBy(Date filterDate, Long amount, String name, Long projectId, Long divisionId);
}
#Service
public class EntityAServiceImpl implements EntityAService {
#Autowired
EntityARepository entityARepository;
#Override
public List<Object[]> findAllBy(Date filterDate, Long amount, String name, Long projectId, Long divisionId) {
...
...
...
}
}
And by using Custom Repositories all becomes like
public interface EntityACustomRepository {
List<Object[]> findAllBy(Date filterDate, Long amount, String name, Long projectId, Long divisionId);
}
public interface EntityARepository extends JpaRepository<EntityA, Long> {
//#Query(...)
//List<EntityA> findAllBy(Date filterDate, Long amount, String name, Long projectId, Long divisionId);
}
#Repository
public class EntityACustomRepositoryImpl implements EntityACustomRepository {
// autowiring entityManager helped to create and execute dynamic jpql
#Autowired
EntityManager entityManager;
public List<Object[]> findAllBy(Date filterDate, Long amount, String name, Long projectId, Long divisionId) {
String jpql = "SELECT " +
" ef, ed, ea " +
" FROM EntityA ea " +
" JOIN EntityB eb " +
" JOIN EntityC ec " +
" JOIN EntityD ed " +
" JOIN EntityE ee " +
" JOIN EntityF ef " +
" WHERE " +
" TRUNC(ee.date) = TRUNC(:date) "
;
//conditions based on screen filter parameters
if(amount!=null && amount>0L) {
jpql += " AND ef.amount = :amount ";
}
if(name!=null && name.trim().length()>0) {
jpql += " AND LOWER(ec.name) LIKE LOWER('%' || :name || '%') ";
}
if(projectId!=null && projectId>0L) {
jpql += " AND ec.projectId = :projectId ";
}
if(divisionId!=null && divisionId>0L) {
jpql += " AND ed.divisionId = :divisionId ";
}
Query query = entityManager.createQuery(jpql);
query.setParameter("date", filterDate);
if(amount!=null && amount>0L) {
query.setParameter("amount", amount);
}
if(name!=null && name.trim().length()>0) {
query.setParameter("name", name);
}
if(projectId!=null && projectId>0L) {
query.setParameter("projectId", projectId);
}
if(divisionId!=null && divisionId>0L) {
query.setParameter("divisionId", divisionId);
}
return query.getResultList();
}
}
public interface EntityAService {
List<Object[]> findAllBy(Date filterDate, Long amount, String name, Long projectId, Long divisionId);
}
#Service
public class EntityAServiceImpl implements EntityAService {
#Autowired
EntityARepository entityARepository;
#Override
public List<Object[]> findAllBy(Date filterDate, Long amount, String name, Long projectId, Long divisionId) {
return entityARepository.findAllBy(filterDate, amount, name, projectId, divisionId);
}
}

It is possible to query on child entities using the Specification API if you map them with JPA, e.g. with #OneToMany.
#Entity
#Table(...)
public class EntityA {
// Omitting fields
#OneToMany(...)
private List<EntityB> bList = new ArrayList<>();
}
public class EntityASpecification implements Specification<EntityA> {
private SearchCriteriaValueClass criteria;
public EntityASpecification(SearchCriteriaValueClass criteria) {
this.criteria = criteria;
}
#Override
public Predicate toPredicate(Root<EntityA> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
ListJoin<EntityA, EntityB> pathToEntityB = root.join(EntityA_.bList, JoinType.INNER);
Predicate amountInBIsEqual = builder.equal(pathToEntityB.get(EntityB_.amount), criteria.getAmount);
Path<SomeEntityAField> pathToAField = root.get(EntityA_.someAField);
Predicate someValueInA = pathToAField.in(criteria.getCollectionForAFieldToBeIn());
query.distinct(true);
return builder.and(amountInBIsEqual, someValueInA);
}
}
Obviously a Specification is defined for one Entity and can only return instances of that. I don't see any (safe and effective) way of returning instances of F, D and A in one method call other than them being connected in a has-a relationship.

Related

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

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

JPA Query method not returning anything

I currently have it working with a custom n1ql query, however it's such a simple query, I figured I could just use the built in jpa query method, however I can't figure out the key words, because I'm not getting anything back.
This code works:
#Query("SELECT meta().id as _ID, meta().cas as _CAS, * FROM `my-bucket` mb " +
"WHERE mb.name like $1 OR ANY Parent " +
"IN mb.Parents SATISFIES Parent.name like $1 END")
List<MyObject> searchObjectByName(String name);
This however doesn't work
#N1qlPrimaryIndexed
public interface MyObjectRepository extends CouchbasePagingAndSortingRepository<MyObject, String> {
List<MyObject> findBySecondObjectNameContains(String name);
}
#Data
#Document
public class MyObject{
#Id
private String objectId;
#Field
private SecondObject secondObject;
}
#Data
public class SecondObject {
#Field
private String name;
}
My test method:
#Autowired
private MyObjectRepository myObjectRepository;
#Test
public void testFind() {
List<MyObject> myObjects = myObjectRepository.findBySecondObjectNameContains("my name");
Assert.assertNotNull(myObjects);
}
The query looks correct, a few things that might be missing:
1) In your test, are you sure that "my name" isn't supposed to be "my name%"?
2) Check if you have a primary or secondary index that covers this query (run the same query via web console)
3) When did you insert the data? If you haven't configured couchbase to be strong consistent, you might have been reading an old version of your data

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;
}
}

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)