Jak pobrać listę obiektów @ManyToMany JPA

0

Witam posiadam dwie tabele stworzone poprzez klasy Encji połączone ze sobą Wiele do wielu

package pl.luppo.ztz.model;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;

@Entity
@NamedQueries({ @NamedQuery(name = Purchase.ALL, query = "SELECT p FROM Purchase p  "),
		@NamedQuery(name = Purchase.TOTAL, query = "SELECT COUNT(p) FROM Purchase p") })
public class Purchase extends AbstractEntity implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	public final static String ALL = "PURCHASE_ALL";
	public final static String TOTAL = "PURCHASE_TOTAL";

	private Double sum;
	private Date orderDate;
	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "COMPANY_ID")
	private Company company;
	@ManyToMany(cascade = { CascadeType.ALL })
	@JoinTable(name = "PUR_PROD", joinColumns = { @JoinColumn(name = "PUR_ID") }, inverseJoinColumns = { @JoinColumn(name = "PRO_ID") })
	private List<Product> products;

	public Double getSum() {
		return sum;
	}

	public void setSum(Double sum) {
		this.sum = sum;
	}

	public Company getCompany() {
		return company;
	}

	public void setCompany(Company company) {
		this.company = company;
	}

	public List<Product> getProducts() {
		return products;
	}

	public void setProducts(List<Product> products) {
		this.products = products;
	}

	public Date getOrderDate() {
		return orderDate;
	}

	public void setOrderDate(Date orderDate) {
		this.orderDate = orderDate;
	}

}

oraz

package pl.luppo.ztz.model;

import java.io.Serializable;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;

@Entity
@NamedQueries({
	@NamedQuery(name = Product.ALL, query = "SELECT p FROM Product p  "),
    @NamedQuery(name = Product.TOTAL, query = "SELECT COUNT(p) FROM Product p")})
public class Product extends AbstractEntity implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	public final static String ALL = "PRODUCT_ALL";
	public final static String TOTAL = "PRODUCT_TOTAL";


	private Double price;
	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "MANUFACTURER_ID")
	private Manufacturer manufacturer;
	@ManyToMany(cascade = { CascadeType.ALL })
	@JoinTable(name = "PUR_PROD", joinColumns = { @JoinColumn(name = "PRO_ID") }, inverseJoinColumns = { @JoinColumn(name = "PUR_ID") })
	private List<Purchase> orders;
	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "CATEGOTY_ID")
	private Category category;



	public Double getPrice() {
		return price;
	}

	public void setPrice(Double price) {
		this.price = price;
	}



	public Manufacturer getManufacturer() {
		return manufacturer;
	}

	public void setManufacturer(Manufacturer manufacturer) {
		this.manufacturer = manufacturer;
	}

	public List<Purchase> getOrders() {
		return orders;
	}

	public void setOrders(List<Purchase> orders) {
		this.orders = orders;
	}

	public Category getCategory() {
		return category;
	}

	public void setCategory(Category category) {
		this.category = category;
	}



}

Id w obu przypadkach jest w klasie nadrzędnej AbstractEntity

Metoda która służy do zapisania obiektu purchase wygląda

public String acceptAction() {
		purchase = new Purchase();
		Double sum = 0D;
		if (login.getProducts() != null) {
			for (Product tmpProduct : login.getProducts()) {
				sum = sum + tmpProduct.getPrice();
			}
			purchase.setSum(sum);
			purchase.setProducts(login.getProducts());
			purchase.setCompany(login.getCompany());
			purchase.setOrderDate(new Date());
			purchaseService.merge(purchase);
		}
		return "/web/shop/purchaseView.xhtml";
	}

Po zakończeniu metody dodawania do bazy wszystko jest ok zostaje dodane tyle produktów ile wcześniej sobie wybiorę ale gdy chce później za pomocą wybrania rekordu zakupu pokazać wszystkie produkty to lista jest pusta

	public List<Product> products() {
		 if (selectedPurchase != null) {
		 return selectedPurchase.getProducts();
		 }
		 return null;
	}

Cały obiekt selectedPurchase jest poprawnie wypełniony oprócz listy produktów co prawda nie jest ona nullem ale nie są to produkty które zostały przypisane do zakupu
Próbowałem również dociągnąć je z bazy i też nie zadziałało

                         public List<Product> findProductsByPurchase(Long purchaseId){
			CriteriaBuilder builder = entityManager.getCriteriaBuilder();
			CriteriaQuery<Product> query = builder.createQuery(Product.class);
			Root<Product> root = query.from(Product.class);
			Join<Product, Purchase> purchase = root.join("orders", JoinType.LEFT);
			Predicate predicate = builder.conjunction();
			query.select(root);
			
			if(purchaseId != null){
				predicate = builder.and(predicate, builder.equal(purchase.get("id"), purchaseId));
			}
			
			query.where(predicate);
			return entityManager.createQuery(query).getResultList();
		}

Proszę o pomoc jak moę pobrać listę produktów z danego zakupu

0
  1. Jesteś pewien że nie pochrzaniłes tych ręcznie robionych Joinów? Ile razy takie widzę zawsze ktoś (zupełnie niepotrzebnie...) robi je źle.
  2. Nie mapowałbym tego jako List<T> tylko jako Set<T>, bo sprawia to często kłopoty.
  3. Kwestia stylu: nie powinieneś robić takich operacji jak składanie obiektu encyjnego i wołanie DAO w kontrolerze, tylko w jakimś serwisie ;]
  4. Włącz w konfiguracji dostawcy JPA logowanie SQLa i zobaczysz jakie zapytanie ci generuje.
0
  1. Wydaje mi się że poprawnie złączyłem te tabele nie znam innego sposobu jeśli jesteś w stanie podpowiedzieć jakieś inne poprawne połączenie byłbym wdzięczny.
    2 Mapuje na Listach właśnie dlatego że kiedyś miałem problem gdy mapowałem na Set-ach zmieniłem na List i jak ręką odjął, dlatego tak już zostawiłem.
  2. Czyli składanie obiektów w kontrolerze nie jest poprawne? Powinienem utworzyć zwykłą klasę i w parametrze konstruktora przekazywać wszystkie parametry do złożenia obiektu ? Pytam ponieważ nie za bardzo wiem o co Ci chodzi a jak robię źle to chętnie to zmienię.
 Hibernate: select product0_.id as id20_, product0_.name as name20_, product0_.CATEGOTY_ID as CATEGOTY4_20_, product0_.MANUFACTURER_ID as MANUFACT5_20_, product0_.price as price20_ from Product product0_ left outer join PUR_PROD orders1_ on product0_.id=orders1_.PRO_ID left outer join Purchase purchase2_ on orders1_.PUR_ID=purchase2_.id where 1=1 and purchase2_.id=45

Korzystając z tego że już założyłem post proszę powiedz mi jeszcze jaka jest adnotacja która pozwala na stworzenie pola w klasie encji i jest to pole opisowe nie jest przenoszone na tabele. Pole jest niewidoczne podczas zakładania tabeli

0
  1. Innym sposobem jest nie dodawanego żadnych dodatkowych adnotacji (oprócz manytoone / onetomany). JPA sobie poradzi bez tego.
  2. No tak, doskonały sposób. Nie wiem czemu nie działa to dokonam losowej zmiany w kodzie ;]
  3. Nie jest. Kontroler powinien zajmować się właściwie tylko obsłują żądania i zwróceniem odpowiedniego wiodku a na pewno nie operacjami na bazie danych. Powinieneś mieć osobny serwis, wstrzyknięty do tego kontrolera, który by sie tym zajmował.
  4. Ale co ci wygenerowało takie zapytanie? Bo ja myślałem ze chcesz z Purchase wypisać produkty i coś ci nie działa, ale to zapytanie nijak sie do tego ma...
0
  1. Błąd miałem już ponad rok temu w jakimś innym zadaniu i dla tego nie jestem wstanie dokładnie określić co to był za błąd ale na pewno związany z tym Setem.

W kontrolerze tylko odwołuję się do serwisu

package pl.luppo.server;

import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import pl.luppo.ztz.model.Company;

/**
 * Klasa umozliwiająca podstawowe działania na bazie
 * @author Luppo
 *
 * @param <T>
 */
public abstract class DataAccessService<T> {
	 
	@PersistenceContext(name="industry",type=PersistenceContextType.EXTENDED)
    private EntityManager em;
 
    public DataAccessService() {
    }
 
    private Class<T> type;
 
    /**
     * Default constructor
     *
     * @param type entity class
     */
    public DataAccessService(Class<T> type) {
        this.type = type;
    }
 
    /**
     * Stores an instance of the entity class in the database
     * @param T Object
     * @return
     */
    public T create(T t) {
        this.em.persist(t);
        this.em.flush();
        this.em.refresh(t);
        return t;
    }
    
    public T merge (T t){
    	t = this.em.merge(t);
    	return t;
    	
    }
 
    /**
     * Retrieves an entity instance that was previously persisted to the database
     * @param T Object
     * @param id
     * @return
     */
    public T find(Object id) {
        return this.em.find(this.type, id);
    }
 
    /**
     * Removes the record that is associated with the entity instance
     * @param type
     * @param id
     */
    public void delete(Object id) {
        Object ref = this.em.getReference(this.type, id);
        this.em.remove(ref);
    }
 
    /**
     * Removes the number of entries from a table
     * @param <T>
     * @param items
     * @return
     */
    public boolean deleteItems(T[] items) {
        for (T item : items) {
            em.remove(em.merge(item));
        }
        return true;
    }
 
    /**
     * Updates the entity instance
     * @param <T>
     * @param t
     * @return the object that is updated
     */
    public T update(T t) {
        return (T) this.em.merge(t);
    }
 
    /**
     * Returns the number of records that meet the criteria
     * @param namedQueryName
     * @return List
     */
    public List findWithNamedQuery(String namedQueryName) {
        return this.em.createNamedQuery(namedQueryName).getResultList();
    }
 
    /**
     * Returns the number of records that meet the criteria
     * @param namedQueryName
     * @param parameters
     * @return List
     */
    public List findWithNamedQuery(String namedQueryName, Map parameters) {
        return findWithNamedQuery(namedQueryName, parameters, 0);
    }
 
    /**
     * Returns the number of records with result limit
     * @param queryName
     * @param resultLimit
     * @return List
     */
    public List findWithNamedQuery(String queryName, int resultLimit) {
        return this.em.createNamedQuery(queryName).
                setMaxResults(resultLimit).
                getResultList();
    }
 
    /**
     * Returns the number of records that meet the criteria
     * @param <T>
     * @param sql
     * @param type
     * @return List
     */
    public List<T> findByNativeQuery(String sql) {
        return this.em.createNativeQuery(sql, type).getResultList();
    }
 
    /**
     * Returns the number of total records
     * @param namedQueryName
     * @return int
     */
    public int countTotalRecord(String namedQueryName) {
        Query query = em.createNamedQuery(namedQueryName);
        Number result = (Number) query.getSingleResult();
        return result.intValue();
    }
 
    /**
     * Returns the number of records that meet the criteria with parameter map and
     * result limit
     * @param namedQueryName
     * @param parameters
     * @param resultLimit
     * @return List
     */
    public List findWithNamedQuery(String namedQueryName, Map parameters, int resultLimit) {
        Set<Map.Entry<String, Object>> rawParameters = parameters.entrySet();
        Query query = this.em.createNamedQuery(namedQueryName);
        if (resultLimit > 0) {
            query.setMaxResults(resultLimit);
        }
        for (Map.Entry<String, Object> entry : rawParameters) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
        return query.getResultList();
    }
 
    /**
     * Returns the number of records that will be used with lazy loading / pagination
     * @param namedQueryName
     * @param start
     * @param end
     * @return List
     */
    public List findWithNamedQuery(String namedQueryName, int start, int end) {
        Query query = this.em.createNamedQuery(namedQueryName);
        query.setMaxResults(end - start);
        query.setFirstResult(start);
        return query.getResultList();
    }
    
    public List<T> findAllForClass(){
		CriteriaBuilder builder = em.getCriteriaBuilder();
		CriteriaQuery<T> query = builder.createQuery(type);
		Root<T> root = query.from(type);
		query.select(root);
		return em.createQuery(query).getResultList();
	}
    public List<T> findByName(Long id, String name){
    	CriteriaBuilder builder = em.getCriteriaBuilder();
    	CriteriaQuery<T> query = builder.createQuery(type);
    	Predicate predicate = builder.conjunction();
    	Root<T> root = query.from(type);
    	query.select(root);
    	if(id != null){
    		predicate = builder.and(predicate, builder.notEqual(root.get("id"), id));
    	}
    	if(name != null){
    		predicate = builder.and(predicate, builder.equal(root.get("name"), name));
    	}
    	query.where(predicate);
    	return em.createQuery(query).getResultList();
    }
    

}

lub do

package pl.luppo.server;

import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import pl.luppo.ztz.model.Company;
import pl.luppo.ztz.model.Product;
import pl.luppo.ztz.model.Purchase;

@Stateless
public class ProductDAO {
	@PersistenceContext(name="industry",type=PersistenceContextType.EXTENDED)
    private EntityManager entityManager;
	
	public List<Product> findProductsByPurchase(Long purchaseId){
			CriteriaBuilder builder = entityManager.getCriteriaBuilder();
			CriteriaQuery<Product> query = builder.createQuery(Product.class);
			Root<Product> root = query.from(Product.class);
			Join<Product, Purchase> purchase = root.join("orders", JoinType.LEFT);
			Predicate predicate = builder.conjunction();
			query.select(root);
			
			if(purchaseId != null){
				predicate = builder.and(predicate, builder.equal(purchase.get("id"), purchaseId));
			}
			query.where(predicate);
			return entityManager.createQuery(query).getResultList();
		}
}

Mam nadzieję że o coś takiego chodzi

Tak dokładnie chce wypisać produkty teraz zauważyłem że błąd nie leży po stronie zapytania tylko formatki przesyła zawsze jeden i ten sam id nawet gdy klikam w różne wiersze i pech chciał że jest to id nie połączone z produktami. Teraz muszę sprawdzić gdzie mam błąd na formatce że przesyłany jest zawsze pierwszy obiekt z tabeli.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:p="http://primefaces.org/ui">
<h:head>
	<title>My JSF Page</title>
</h:head>
<h:body>
	<h:form id="list">
		<p:growl id="growl" showDetail="true" sticky="true" />
		<p:panel header="#{purRes.order_history}">
			<p:dataTable var="purchase" value="#{purchaseHome.purchases()}"
				paginator="true" rows="10" rowKey="#{purchase.company.name}"
				paginatorTemplate="{CurrentPageReport}  {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
				rowsPerPageTemplate="5,10,15" selectionMode="single"
				selection="#{purchaseHome.selectedPurchase}" id="purchaseTable">
				<p:ajax event="rowSelect" update=":form"
					oncomplete="PF('dlg').show()"  />

				<p:column headerText="#{menRes.id}">
					<h:outputText value="#{purchase.id}" />
				</p:column>
				<p:column headerText="#{proRes.price}">
					<h:outputText value="#{purchase.sum}" />
				</p:column>
				<p:column headerText="#{purRes.order_date}">
					<h:outputText value="#{purchase.orderDate}">
						<f:convertDateTime pattern="MM/dd/yyyy HH:mm:ss" />
					</h:outputText>
				</p:column>
				<p:column headerText="#{menRes.setting}"
					style="width:60px; text-align:center"
					rendered="#{companyHome.logIN}">
					<p:commandButton ajax="true" 
						action="#{purchaseHome.editAction(purchase)}" icon="ui-icon-trash"
						title="#{proRes.buy}" />
				</p:column>
			</p:dataTable>
			<p:commandButton ajax="true" value="#{menRes.close}"
				action="#{productHome.closeAction()}" />
		</p:panel>
	</h:form>
	<h:form id="form">
		<p:dialog id="dialog" header="Car Detail" widgetVar="dlg"
			resizable="false" showEffect="fade" hideEffect="explode">

			<h:panelGrid id="display" columns="2" cellpadding="4">

				<f:facet name="header">
					<h:outputText value="#{purchaseHome.login.company.name}" />
				</f:facet>

				<h:outputText value="#{purRes.order_date}" />
				<h:outputText value="#{purchaseHome.selectedPurchase.orderDate}" />

				<h:outputText value="#{purRes.order_sum}" />
				<h:outputText value="#{purchaseHome.selectedPurchase.sum}" />
			</h:panelGrid>
			<p:dataTable value="#{purchaseHome.products()}"
				var="product" paginator="true" rows="10" rowKey="#{product.name}"
				paginatorTemplate="{CurrentPageReport}  {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
				rowsPerPageTemplate="5,10,15" id="purchaseTable">
				<p:column headerText="#{menRes.name}">
					<h:outputText value="#{product.name}" />
				</p:column>
				<p:column headerText="proRes.price">
					<h:outputText value="#{product.price}" />
				</p:column>
				<p:column headerText="proRes.manufacturer">
					<h:outputText value="#{product.manufacturer}" />
				</p:column>
			</p:dataTable>
		</p:dialog>
	</h:form>
</h:body>
</html>

Z góry dziękuje za pomoc

<p:ajax event="rowSelect"  update=":form:dialog :form:display" oncomplete="PF('dlg').show()" >
				<f:setPropertyActionListener target="#{purchaseHome.selectedPurchase}" value="#{purchase}" />
</p:ajax>

Dodałem przycisk w wierszu i działa poprawnie

<p:commandButton ajax="true" 
					 icon="ui-icon-search" oncomplete="PF('dlg').show()"
						title="#{proRes.buy}" update=" :form:dialog :form:display">
						<f:setPropertyActionListener target="#{purchaseHome.selectedPurchase}" value="#{purchase}" />
						</p:commandButton>
0

Z tym serwisem to chodziło mi o to:

        purchase = new Purchase();
        Double sum = 0D;
        if (login.getProducts() != null) {
            for (Product tmpProduct : login.getProducts()) {
                sum = sum + tmpProduct.getPrice();
            }
            purchase.setSum(sum);
            purchase.setProducts(login.getProducts());
            purchase.setCompany(login.getCompany());
            purchase.setOrderDate(new Date());
            purchaseService.merge(purchase);
        }

To nie jest kawałek kodu który powinien być w kontrolerze ;) To jest logika biznesowa i powinna być w osobnym serwisie.

0

Ok teraz rozumiem :) dzięki

1 użytkowników online, w tym zalogowanych: 0, gości: 1