как написать JPA-запрос

Учимся писать JPA-запросы. Пожалуйста, сообщите мне, можно ли более эффективно написать приведенные ниже запросы, возможно, в одном операторе выбора. Может быть соединение, но не уверен, как это сделать.

class Relationship {

  @ManyToOne
  public String relationshipType;  //can be MANAGER, CUSTOMER etc

  @ManyToOne
  public Party partyFrom; // a person who has a relation

  @ManyToOne
  public Party partyTo; // a group a person relate to
}

Запросы:

        String sql = "";
        sql = "select rel.partyTo";
        sql += " from Relationship rel";
        sql += " where rel.partyFrom = :partyFrom";
        sql += " and rel.relationshipType= :typeName";
        Query query = Organization.em().createQuery(sql);
        query.setParameter("partyFrom", mgr1);
        query.setParameter("typeName", "MANAGER");
        List<Party> orgList = query.getResultList();

        String sql2 = "";
        sql2 = "select rel.partyFrom";
        sql2 += " from Relationship rel";
        sql2 += " where rel.partyTo = :partyToList";
        sql2 += " and rel.relationshipType = :typeName2";
        Query query2 = Organization.em().createQuery(sql2);
        query2.setParameter("partyToList", orgList);
        query2.setParameter("typeName2", "CUSTOMER");
        List<Party> personList2 = query2.getResultList();

Оба запроса работают. Запрос 1 возвращает список групп, с которыми человек (mgr1) имеет отношения MANAGER. Запрос 2 возвращает всех Лиц, которым они являются КЛИЕНТОМ, в группы, возвращенные запросом 1. Фактически, я получаю список Лиц, к которым они принадлежат (клиенты), той же группы, с которой Человек (mgr1) имеет отношение МЕНЕДЖЕР.

Можно ли объединить их в один оператор sql, чтобы, возможно, был доступен только один доступ к базе данных?


person bsr    schedule 29.07.2010    source источник


Ответы (2)


Вы буквально вкладываете один запрос в другой и используете предложение «где в», чтобы указать, что внешний запрос должен извлекать клиентов из внутреннего запроса.

select rel2.partyFrom
from Relationship rel2
where rel2.relationshipType = :typeName2 /* customer */
and rel2.partyTo.id in 
      (select rel.partyTo.id
      from Relationship rel
      where rel.partyFrom = :partyFrom
      and rel.relationshipType = :typeName)

Ваш вызывающий код передает параметры typeName, typeName2 и partyFrom, как и раньше. Параметр PartyTo не нужен, так как данные поступают из подзапроса (внутренний запрос).

Вы можете добиться того же результата, используя самообъединение с предложением where, которое фильтрует менеджеров слева, а клиентов справа, но использование предложения in семантически более ясно.

РЕДАКТИРОВАТЬ: я добавил .id в подвыборку, что, по моему мнению, необходимо.

person mdma    schedule 29.07.2010
comment
спасибо!!.. сработало отлично (без идентификатора - РЕДАКТИРОВАТЬ).. приму ответ, подождите, чтобы увидеть, прокомментирует ли кто-нибудь производительность внутреннего запроса.. является ли это оптимальным способом.. еще раз спасибо.. - person bsr; 29.07.2010
comment
Подзапрос будет быстрым, поскольку это не коррелированный запрос — он вычисляется только один раз, как и в исходном коде. Только сегодня я заменил LEFT JOIN на WHERE IN и изменил производительность запроса с 18 до 5 на SQL-сервере. Вы не говорите, какую базу данных вы используете, но в целом подзапрос может превзойти соединение, когда вы выполняете только проверку существования и не используете какие-либо данные. - person mdma; 29.07.2010
comment
спасибо .. планирую использовать PostgreSQL или Oracle .. Я использую Hibernate ORM, поэтому, возможно, он может быть совместим и с другими .. не могли бы вы уточнить тест на существование по сравнению с неиспользованием каких-либо данных .. каждый день изучаю новые вещи: - ) спасибо.. - person bsr; 29.07.2010
comment
При объединении вы обычно делаете это, чтобы ввести другие поля из объединенной таблицы. Некоторые люди также используют внешнее соединение как своего рода проверку существования — существует ли этот ключ в наборе объединенных значений. Но поскольку ни одно из других полей из объединенной таблицы не требуется, гораздо эффективнее использовать предложение WHERE IN. - person mdma; 29.07.2010

Это не ответ на вопрос, а помощь другим людям в случае, если кто-то ищет отношение @OneToMany в Spring Data JPA с использованием JPQL, потому что вопрос связан с JPA, поэтому решил поделиться своими 2 центами, заранее извиняюсь

@Entity
@Table(name = "MY_CAR")
public class MyCar {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Column(name = "DESCRIPTION")
private String description;

@Column(name = "MY_CAR_NUMBER")
private String myCarNumber;

@Column(name = "RELEASE_DATE")
private Date releaseDate;

@OneToMany(cascade = { CascadeType.ALL })
@JoinTable(name = "MY_CAR_VEHICLE_SERIES", joinColumns = @JoinColumn(name = "MY_CAR_ID "), inverseJoinColumns = @JoinColumn(name = "VEHICLE_SERIES_ID"))
private Set<VehicleSeries> vehicleSeries;
public MyCar() {
    super();
    vehicleSeries = new HashSet<VehicleSeries>();
}
// set and get method goes here


@Entity
@Table(name = "VEHICLE_SERIES ")
public class VehicleSeries {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Column(name = "SERIES_NUMBER")
private String seriesNumber;

@OneToMany(cascade = { CascadeType.ALL })
@JoinTable(name = "VEHICLE_SERIES_BODY_TYPE", joinColumns = @JoinColumn(name = "VEHICLE_SERIES_ID"), inverseJoinColumns = @JoinColumn(name = "BODY_TYPE_ID"))
private Set<BodyType> bodyTypes;
public VehicleSeries() {
    super();
    bodyTypes = new HashSet<BodyType>();
}
// set and get method goes here


@Entity
@Table(name = "BODY_TYPE ")
public class BodyType implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Column(name = "NAME")
private String name;
// set and get method goes here


public interface MyCarRepository extends JpaRepository<MyCar, Long> {
public Set<MyCar> findAllByOrderByIdAsc();

@Query(value = "select distinct myCar from MyCar myCar "
        + "join myCar.vehicleSeries as vs join vs.bodyTypes as bt where vs.seriesNumber like %:searchMyCar% "
        + "or lower(bt.name) like lower(:searchMyCar) or myCar.bulletinId like %:searchMyCar% "
        + "or lower(myCar.description) like lower(:searchMyCar) "
        + "or myCar.bulletinNumber like %:searchMyCar% order by myCar.id asc")
public Set<MyCar> searchByMyCar(@Param("searchMyCar") String searchMyCar);

}

Некоторые данные в таблицах, например

Выберите * из Vehicle_Series

ID      SERIES_NUMBER  
1       Yaris
2       Corolla

Выберите * из Body_Type

ID      NAME  
1       Compact
2       Convertible 
3       Sedan
person Java_Fire_Within    schedule 20.12.2017