JPA: как преобразовать собственный набор результатов запроса в коллекцию классов POJO
Я использую JPA в своем проекте.
Я пришел к запросу, в котором мне нужно выполнить операцию объединения пяти таблиц. Поэтому я создал собственный запрос, который возвращает пять полей.
Теперь я хочу преобразовать полученный объект в Java-класс POJO, который содержит те же пять строк.
Есть ли способ в JPA напрямую привести этот результат в список объектов POJO??
Я пришел к следующему решению..
@NamedNativeQueries({
@NamedNativeQuery(
name = "nativeSQL",
query = "SELECT * FROM Actors",
resultClass = db.Actor.class),
@NamedNativeQuery(
name = "nativeSQL2",
query = "SELECT COUNT(*) FROM Actors",
resultClass = XXXXX) // <--------------- problem
})
Теперь здесь, в resultClass, мы должны предоставить класс, который является фактической сущностью JPA? ИЛИ Мы можем преобразовать его в любой класс JAVA POJO, который содержит те же имена столбцов?
24 ответа
JPA обеспечивает SqlResultSetMapping
это позволяет вам отображать любые результаты из вашего собственного запроса в сущность или пользовательский класс ,
РЕДАКТИРОВАТЬ JPA 1.0 не разрешает сопоставление с не-сущностными классами. Только в JPA 2.1 ConstructorResult был добавлен к возвращаемым значениям карты java-класса.
Кроме того, для проблемы OP с получением количества должно быть достаточно, чтобы определить отображение набора результатов с одним ColumnResult
Я нашел пару решений для этого.
Использование сопоставленных объектов (JPA 2.0)
Используя JPA 2.0, невозможно сопоставить собственный запрос с POJO, это можно сделать только с помощью объекта.
Например:
Query query = em.createNativeQuery("SELECT name,age FROM jedi_table", Jedi.class);
@SuppressWarnings("unchecked")
List<Jedi> items = (List<Jedi>) query.getResultList();
Но в этом случае Jedi
, должен быть сопоставленным классом сущности.
Альтернативой, чтобы избежать неконтролируемого здесь предупреждения, было бы использование именованного собственного запроса. Так что, если мы объявим собственный запрос в сущности
@NamedNativeQuery(
name="jedisQry",
query = "SELECT name,age FROM jedis_table",
resultClass = Jedi.class)
Тогда мы можем просто сделать:
TypedQuery<Jedi> query = em.createNamedQuery("jedisQry", Jedi.class);
List<Jedi> items = query.getResultList();
Это безопаснее, но мы по-прежнему ограничены в использовании сопоставленной сущности.
Ручное картографирование
Решение, которое я немного экспериментировал (до появления JPA 2.1), заключалось в отображении конструктора POJO с использованием небольшого отражения.
public static <T> T map(Class<T> type, Object[] tuple){
List<Class<?>> tupleTypes = new ArrayList<>();
for(Object field : tuple){
tupleTypes.add(field.getClass());
}
try {
Constructor<T> ctor = type.getConstructor(tupleTypes.toArray(new Class<?>[tuple.length]));
return ctor.newInstance(tuple);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Этот метод в основном берет массив кортежей (как возвращено нативными запросами) и сопоставляет его с предоставленным классом POJO, ища конструктор, который имеет такое же количество полей и того же типа.
Тогда мы можем использовать удобные методы, такие как:
public static <T> List<T> map(Class<T> type, List<Object[]> records){
List<T> result = new LinkedList<>();
for(Object[] record : records){
result.add(map(type, record));
}
return result;
}
public static <T> List<T> getResultList(Query query, Class<T> type){
@SuppressWarnings("unchecked")
List<Object[]> records = query.getResultList();
return map(type, records);
}
И мы можем просто использовать эту технику следующим образом:
Query query = em.createNativeQuery("SELECT name,age FROM jedis_table");
List<Jedi> jedis = getResultList(query, Jedi.class);
JPA 2.1 с @SqlResultSetMapping
С появлением JPA 2.1 мы можем использовать аннотацию @SqlResultSetMapping для решения проблемы.
Нам нужно объявить отображение набора результатов где-то в сущности:
@SqlResultSetMapping(name="JediResult", classes = {
@ConstructorResult(targetClass = Jedi.class,
columns = {@ColumnResult(name="name"), @ColumnResult(name="age")})
})
И тогда мы просто делаем:
Query query = em.createNativeQuery("SELECT name,age FROM jedis_table", "JediResult");
@SuppressWarnings("unchecked")
List<Jedi> samples = query.getResultList();
Конечно, в этом случае Jedi
не должен быть сопоставленным объектом. Это может быть обычный POJO.
Использование XML Mapping
Я один из тех, кто находит добавление всех этих @SqlResultSetMapping
довольно инвазивен в моих сущностях, и мне особенно не нравится определение именованных запросов внутри сущностей, поэтому в качестве альтернативы я делаю все это в META-INF/orm.xml
файл:
<named-native-query name="GetAllJedi" result-set-mapping="JediMapping">
<query>SELECT name,age FROM jedi_table</query>
</named-native-query>
<sql-result-set-mapping name="JediMapping">
<constructor-result target-class="org.answer.model.Jedi">
<column name="name" class="java.lang.String"/>
<column name="age" class="java.lang.Integer"/>
</constructor-result>
</sql-result-set-mapping>
И это все решения, которые я знаю. Последние два являются идеальным способом, если мы можем использовать JPA 2.1.
Да, с JPA 2.1 это легко. У вас есть очень полезные аннотации. Они упрощают вашу жизнь.
Сначала объявите ваш собственный запрос, а затем сопоставление набора результатов (которое определяет отображение данных, возвращаемых базой данных в ваши POJO). Напишите ваш класс POJO для ссылки (не включен здесь для краткости). И последнее, но не менее важное: создайте метод в DAO (например) для вызова запроса. Это работало для меня в приложении dropwizard (1.0.0).
Сначала объявите собственный запрос в классе сущности:
@NamedNativeQuery (
name = "domain.io.MyClass.myQuery",
query = "Select a.colA, a.colB from Table a",
resultSetMapping = "mappinMyNativeQuery") // must be the same name as in the SqlResultSetMapping declaration
Ниже вы можете добавить объявление отображения набора результатов:
@SqlResultSetMapping(
name = "mapppinNativeQuery", // same as resultSetMapping above in NativeQuery
classes = {
@ConstructorResult(
targetClass = domain.io.MyMapping.class
columns = {
@ColumnResult( name = "colA", type = Long.class),
@ColumnResult( name = "colB", type = String.class)
}
)
}
)
Позже в DAO вы можете ссылаться на запрос как
public List<domain.io.MyMapping> findAll() {
return (namedQuery("domain.io.MyClass.myQuery").list());
}
Вот и все.
Если вы используете Spring-jpa
Это дополнение к ответам и на этот вопрос. Пожалуйста, исправьте это, если есть какие-либо недостатки. Я в основном использовал три метода для достижения "результата картирования Object[]
в pojo", исходя из того, что практическая необходимость я встречаю:
- JPA встроенный метод достаточно.
- Встроенный метод JPA не достаточно, но индивидуальный
sql
с этимиEntity
достаточно. Первый 2 не удалось, и я должен использовать
nativeQuery
, Вот примеры. Пойо ожидал:public class Antistealingdto { private String secretKey; private Integer successRate; // GETTERs AND SETTERs public Antistealingdto(String secretKey, Integer successRate) { this.secretKey = secretKey; this.successRate = successRate; } }
Метод 1: Измените pojo в интерфейс:
public interface Antistealingdto {
String getSecretKey();
Integer getSuccessRate();
}
И хранилище:
interface AntiStealingRepository extends CrudRepository<Antistealing, Long> {
Antistealingdto findById(Long id);
}
Метод 2: Репозиторий:
@Query("select new AntistealingDTO(secretKey, successRate) from Antistealing where ....")
Antistealing whatevernamehere(conditions);
Примечание: последовательность параметров конструктора POJO должна быть одинаковой как в определении POJO, так и в sql.
Способ 3: использование @SqlResultSetMapping
а также @NamedNativeQuery
в Entity
как пример в ответе Эдвина Далорсо.
Первые два метода вызвали бы много промежуточных обработчиков, таких как настраиваемые преобразователи. Например, AntiStealing
определяет secretKey
перед сохранением вставляется конвертер для его шифрования. Это приведет к тому, что первые 2 метода вернут преобразованный обратно secretKey
что не то, что я хочу. Пока способ 3 преодолеет конвертер и вернется secretKey
будет таким же, как он хранится (зашифрованный).
Самый простой способ - использовать проекции. Он может отображать результаты запросов непосредственно в интерфейсы и его легче реализовать, чем с помощью SqlResultSetMapping.
Пример показан ниже:
@Repository
public interface PeopleRepository extends JpaRepository<People, Long> {
@Query(value = "SELECT p.name AS name, COUNT(dp.people_id) AS count " +
"FROM people p INNER JOIN dream_people dp " +
"ON p.id = dp.people_id " +
"WHERE p.user_id = :userId " +
"GROUP BY dp.people_id " +
"ORDER BY p.name", nativeQuery = true)
List<PeopleDTO> findByPeopleAndCountByUserId(@Param("userId") Long userId);
@Query(value = "SELECT p.name AS name, COUNT(dp.people_id) AS count " +
"FROM people p INNER JOIN dream_people dp " +
"ON p.id = dp.people_id " +
"WHERE p.user_id = :userId " +
"GROUP BY dp.people_id " +
"ORDER BY p.name", nativeQuery = true)
Page<PeopleDTO> findByPeopleAndCountByUserId(@Param("userId") Long userId, Pageable pageable);
}
// Interface to which result is projected
public interface PeopleDTO {
String getName();
Long getCount();
}
Поля из проектируемого интерфейса должны соответствовать полям в этом объекте. В противном случае отображение поля может нарушиться.
Также, если вы используете SELECT table.column
нотация всегда определяет псевдонимы, совпадающие с именами из объекта, как показано в примере.
Процедура развертывания может быть выполнена для назначения результатов не-сущности (то есть Beans/POJO). Процедура заключается в следующем.
List<JobDTO> dtoList = entityManager.createNativeQuery(sql)
.setParameter("userId", userId)
.unwrap(org.hibernate.Query.class).setResultTransformer(Transformers.aliasToBean(JobDTO.class)).list();
Использование для реализации JPA-Hibernate.
В спящем режиме вы можете использовать этот код, чтобы легко сопоставить ваш собственный запрос.
private List < Map < String, Object >> getNativeQueryResultInMap() {
String mapQueryStr = "SELECT * FROM AB_SERVICE three ";
Query query = em.createNativeQuery(mapQueryStr);
NativeQueryImpl nativeQuery = (NativeQueryImpl) query;
nativeQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List < Map < String, Object >> result = query.getResultList();
for (Map map: result) {
System.out.println("after request ::: " + map);
}
return result;}
Сначала объявите следующие аннотации:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NativeQueryResultEntity {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NativeQueryResultColumn {
int index();
}
Затем аннотируйте ваш POJO следующим образом:
@NativeQueryResultEntity
public class ClassX {
@NativeQueryResultColumn(index=0)
private String a;
@NativeQueryResultColumn(index=1)
private String b;
}
Затем напишите аннотацию процессора:
public class NativeQueryResultsMapper {
private static Logger log = LoggerFactory.getLogger(NativeQueryResultsMapper.class);
public static <T> List<T> map(List<Object[]> objectArrayList, Class<T> genericType) {
List<T> ret = new ArrayList<T>();
List<Field> mappingFields = getNativeQueryResultColumnAnnotatedFields(genericType);
try {
for (Object[] objectArr : objectArrayList) {
T t = genericType.newInstance();
for (int i = 0; i < objectArr.length; i++) {
BeanUtils.setProperty(t, mappingFields.get(i).getName(), objectArr[i]);
}
ret.add(t);
}
} catch (InstantiationException ie) {
log.debug("Cannot instantiate: ", ie);
ret.clear();
} catch (IllegalAccessException iae) {
log.debug("Illegal access: ", iae);
ret.clear();
} catch (InvocationTargetException ite) {
log.debug("Cannot invoke method: ", ite);
ret.clear();
}
return ret;
}
// Get ordered list of fields
private static <T> List<Field> getNativeQueryResultColumnAnnotatedFields(Class<T> genericType) {
Field[] fields = genericType.getDeclaredFields();
List<Field> orderedFields = Arrays.asList(new Field[fields.length]);
for (int i = 0; i < fields.length; i++) {
if (fields[i].isAnnotationPresent(NativeQueryResultColumn.class)) {
NativeQueryResultColumn nqrc = fields[i].getAnnotation(NativeQueryResultColumn.class);
orderedFields.set(nqrc.index(), fields[i]);
}
}
return orderedFields;
}
}
Используйте вышеуказанные рамки следующим образом:
String sql = "select a,b from x order by a";
Query q = entityManager.createNativeQuery(sql);
List<ClassX> results = NativeQueryResultsMapper.map(q.getResultList(), ClassX.class);
Использование Hibernate:
@Transactional(readOnly=true)
public void accessUser() {
EntityManager em = repo.getEntityManager();
org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
org.hibernate.SQLQuery q = (org.hibernate.SQLQuery) session.createSQLQuery("SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u").addScalar("username", StringType.INSTANCE).addScalar("name", StringType.INSTANCE).addScalar("email", StringType.INSTANCE).addScalar("passe", StringType.INSTANCE).addScalar("loginType", IntegerType.INSTANCE)
.setResultTransformer(Transformers.aliasToBean(User2DTO.class));
List<User2DTO> userList = q.list();
}
Мы решили проблему следующим образом:
//Add actual table name here in Query
final String sqlQuery = "Select a.* from ACTORS a"
// add your entity manager here
Query query = entityManager.createNativeQuery(sqlQuery,Actors.class);
//List contains the mapped entity data.
List<Actors> list = (List<Actors>) query.getResultList();
Поскольку другие уже упомянули все возможные решения, я делюсь своим решением.
В моей ситуации с Postgres 9.4
во время работы с Jackson
,
//Convert it to named native query.
List<String> list = em.createNativeQuery("select cast(array_to_json(array_agg(row_to_json(a))) as text) from myschema.actors a")
.getResultList();
List<ActorProxy> map = new ObjectMapper().readValue(list.get(0), new TypeReference<List<ActorProxy>>() {});
Я уверен, что вы можете найти то же самое для других баз данных.
Также FYI, JPA 2.0 собственные результаты запроса в виде карты
Старый стиль с использованием ResultSet
@Transactional(readOnly=true)
public void accessUser() {
EntityManager em = this.getEntityManager();
org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
session.doWork(new Work() {
@Override
public void execute(Connection con) throws SQLException {
try (PreparedStatement stmt = con.prepareStatement(
"SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u")) {
ResultSet rs = stmt.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
for (int i = 1; i <= rsmd.getColumnCount(); i++) {
System.out.print(rsmd.getColumnName(i) + " (" + rsmd.getColumnTypeName(i) + ") / ");
}
System.out.println("");
while (rs.next()) {
System.out.println("Found username " + rs.getString("USERNAME") + " name " + rs.getString("NAME") + " email " + rs.getString("EMAIL") + " passe " + rs.getString("PASSE") + " email " + rs.getInt("LOGIN_TYPE"));
}
}
}
});
}
Если вы хотите сопоставить результат пользовательского запроса непосредственно с сущностью без написания кода для сопоставления, попробуйте этот способ. По моему опыту, это самый удобный способ, но недостатком является потеря преимущества спящего режима ddl-auto:
Отключите проверку гибернации, удалив
hibernate.ddl-auto
. Если этого не сделать, спящий режим может пожаловаться на отсутствие таблицы в базе данных.Создайте pojo с
@Entity
для пользовательского набора результатов без сопоставления таблиц, что-то вроде:@Getter @Setter @Entity public class MyCustomeResult implements Serializable { @Id private Long id; @Column(name = "name") private String name; }
В репозитории используйте объект для сопоставления непосредственно из
query.getResultList()
public List<MyCustomeResult> findByExampleCustomQuery(Long test) { String sql = "select id, name from examples where id =:test"; Query query = entityManager.createNativeQuery(sql, MyCustomeResult.class); return query.setParameter("test", test).getResultList(); }
Не уверен, подходит ли это здесь, но у меня был похожий вопрос и я нашел следующее простое решение / пример для меня:
private EntityManager entityManager;
...
final String sql = " SELECT * FROM STORE "; // select from the table STORE
final Query sqlQuery = entityManager.createNativeQuery(sql, Store.class);
@SuppressWarnings("unchecked")
List<Store> results = (List<Store>) sqlQuery.getResultList();
В моем случае мне пришлось использовать части SQL, определенные в строках где-то еще, поэтому я не мог просто использовать NamedNativeQuery.
Старый стиль с использованием Resultset
@Transactional(readOnly=true)
public void accessUser() {
EntityManager em = this.getEntityManager();
org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
session.doWork(new Work() {
@Override
public void execute(Connection con) throws SQLException {
try (PreparedStatement stmt = con.prepareStatement(
"SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u")) {
ResultSet rs = stmt.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
for (int i = 1; i <= rsmd.getColumnCount(); i++) {
System.out.print(rsmd.getColumnName(i) + " (" + rsmd.getColumnTypeName(i) + ") / ");
}
System.out.println("");
while (rs.next()) {
System.out.println("Found username " + rs.getString("USERNAME") + " name " + rs.getString("NAME") + " email " + rs.getString("EMAIL") + " passe " + rs.getString("PASSE") + " email " + rs.getInt("LOGIN_TYPE"));
}
}
}
});
}
Я пробовал многое из того, что упоминалось в ответах выше. SQLmapper очень запутался в том, где его разместить. Только неуправляемые POJO были проблемой. Я пробовал разные способы, и один простой способ сработал, как обычно. Я использую hibernate-jpa-2.1.
List<TestInfo> testInfoList = factory.createNativeQuery(QueryConstants.RUNNING_TEST_INFO_QUERY)
.getResultList();
Единственное, о чем следует позаботиться, это то, что POJO имеет те же имена переменных-членов, что и запрос (все в нижнем регистре). И, видимо, мне даже не нужно было указывать целевой класс вместе с запросом, как мы делаем с TypedQueries в JPQL.
TestInfo.class
@Setter
@Getter
@NoArgsConstructor
@ToString
public class TestInfo {
private String emailid;
private Long testcount;
public TestInfo(String emailId, Long testCount) {
super();
this.emailid = emailId;
this.testcount = testCount;
}
}
В этом случае очень просто использовать "Просмотр базы данных", например, неизменяемую сущность.
Нормальный объект
@Entity
@Table(name = "people")
data class Person(
@Id
val id: Long = -1,
val firstName: String = "",
val lastName: String? = null
)
Просмотреть как объект
@Entity
@Immutable
@Subselect("""
select
p.id,
concat(p.first_name, ' ', p.last_name) as full_name
from people p
""")
data class PersonMin(
@Id
val id: Long,
val fullName: String,
)
В любом репозитории мы можем создать функцию / метод запроса, например:
@Query(value = "select p from PersonMin p")
fun findPeopleMinimum(pageable: Pageable): Page<PersonMin>
Использование Hibernate:
@Transactional(readOnly=true)
public void accessUser() {
EntityManager em = repo.getEntityManager();
org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
org.hibernate.SQLQuery q = (org.hibernate.SQLQuery) session.createSQLQuery("SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u")
.addScalar("username", StringType.INSTANCE).addScalar("name", StringType.INSTANCE)
.addScalar("email", StringType.INSTANCE).addScalar("passe", StringType.INSTANCE)
.addScalar("loginType", IntegerType.INSTANCE)
.setResultTransformer(Transformers.aliasToBean(User2DTO.class));
List<User2DTO> userList = q.list();
}
Если вы используете Spring, вы можете использовать org.springframework.jdbc.core.RowMapper
Вот пример:
public List query(String objectType, String namedQuery)
{
String rowMapper = objectType + "RowMapper";
// then by reflection you can instantiate and use. The RowMapper classes need to follow the naming specific convention to follow such implementation.
}
Ниже приведен пример использования POJO в качестве псевдо-сущности для получения результата из собственного запроса без использования сложного SqlResultSetMapping. Просто нужно две аннотации, пустая @Enity и фиктивная @Id в вашем POJO. @Id можно использовать в любом поле по вашему выбору, поле @Id может иметь дублирующиеся ключи, но не нулевые значения.
Поскольку @Enity не отображается ни на одну физическую таблицу, этот POJO называется псевдо-сущностью.
Среда: eclipselink 2.5.0-RC1, jpa-2.1.0, mysql-connector-java-5.1.14
Вы можете скачать полный проект Maven здесь
Собственный запрос основан на примере сотрудников MySQL. Ddev http://dev.mysql.com/doc/employee/en/employees-installation.html
persistence.xml
<?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.1"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="jpa-mysql" transaction-type="RESOURCE_LOCAL">
<class>org.moonwave.jpa.model.pojo.Employee</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/employees" />
<property name="javax.persistence.jdbc.user" value="user" />
<property name="javax.persistence.jdbc.password" value="***" />
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
</properties>
</persistence-unit>
Employee.java
package org.moonwave.jpa.model.pojo;
@Entity
public class Employee {
@Id
protected Long empNo;
protected String firstName;
protected String lastName;
protected String title;
public Long getEmpNo() {
return empNo;
}
public void setEmpNo(Long empNo) {
this.empNo = empNo;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("empNo: ").append(empNo);
sb.append(", firstName: ").append(firstName);
sb.append(", lastName: ").append(lastName);
sb.append(", title: ").append(title);
return sb.toString();
}
}
EmployeeNativeQuery.java
public class EmployeeNativeQuery {
private EntityManager em;
private EntityManagerFactory emf;
public void setUp() throws Exception {
emf=Persistence.createEntityManagerFactory("jpa-mysql");
em=emf.createEntityManager();
}
public void tearDown()throws Exception {
em.close();
emf.close();
}
@SuppressWarnings("unchecked")
public void query() {
Query query = em.createNativeQuery("select e.emp_no as empNo, e.first_name as firstName, e.last_name as lastName," +
"t.title from employees e join titles t on e.emp_no = t.emp_no", Employee.class);
query.setMaxResults(30);
List<Employee> list = (List<Employee>) query.getResultList();
int i = 0;
for (Object emp : list) {
System.out.println(++i + ": " + emp.toString());
}
}
public static void main( String[] args ) {
EmployeeNativeQuery test = new EmployeeNativeQuery();
try {
test.setUp();
test.query();
test.tearDown();
} catch (Exception e) {
System.out.println(e);
}
}
}
Простой способ преобразования SQL-запроса в коллекцию классов POJO,
Query query = getCurrentSession().createSQLQuery(sqlQuery).addEntity(Actors.class);
List<Actors> list = (List<Actors>) query.list();
return list;
Все, что вам нужно, это DTO с конструктором:
public class User2DTO implements Serializable {
/** pode ser email ou id do Google comecando com G ou F para Facebook */
private String username;
private String password;
private String email;
private String name;
private Integer loginType;
public User2DTO(Object...fields) {
super();
this.username = (String) fields[0];
this.name = (String) fields[1];
this.email = (String) fields[2];
this.password = (String) fields[3];
this.loginType = (Integer) fields[4];
}
и назовите это:
EntityManager em = repo.getEntityManager();
Query q = em.createNativeQuery("SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u");
List<Object[]> objList = q.getResultList();
List<User2DTO> ooBj = objList.stream().map(User2DTO::new).collect(Collectors.toList());
Использование DTO Design Pattern
, Он был использован в EJB 2.0
, Объект был управляемым контейнером. DTO Design Pattern
используется для решения этой проблемы. Но это может быть использовано сейчас, когда приложение разрабатывается Server Side
а также Client Side
по отдельности.DTO
используется когда Server side
не хочет проходить / возвращаться Entity
с аннотацией к Client Side
,
Пример DTO:
PersonEntity.java
@Entity
public class PersonEntity {
@Id
private String id;
private String address;
public PersonEntity(){
}
public PersonEntity(String id, String address) {
this.id = id;
this.address = address;
}
//getter and setter
}
PersonDTO.java
public class PersonDTO {
private String id;
private String address;
public PersonDTO() {
}
public PersonDTO(String id, String address) {
this.id = id;
this.address = address;
}
//getter and setter
}
DTOBuilder.java
public class DTOBuilder() {
public static PersonDTO buildPersonDTO(PersonEntity person) {
return new PersonDTO(person.getId(). person.getAddress());
}
}
EntityBuilder.java <- это может быть необходимо
public class EntityBuilder() {
public static PersonEntity buildPersonEntity(PersonDTO person) {
return new PersonEntity(person.getId(). person.getAddress());
}
}
Если запрос не слишком сложный, вы можете сделать что-то вроде этого. В моем случае мне нужно было использовать запрос результата H2 FT_Search, чтобы сделать другой запрос.
var ftSearchQuery = "SELECT * FROM FT_SEARCH(\'something\', 0, 0)";
List<Object[]> results = query.getResultList();
List<Model> models = new ArrayList<>();
for (Object[] result : results) {
var newQuery = "SELECT * FROM " + (String) result[0];
models.addAll(entityManager.createNativeQuery(newQuery, Model.class).getResultList());
}
Вероятно, есть более чистый способ сделать это.