Is it possible in plain JPA or Hibernate a composite key, where an element of the composite key is a sequence and the other element is a mapped with a foreign key.
I have a composite key in my table and part of it needs to be generated by a sequence.
I tried the following, but it doesn't work
class produit
#Entity
public class Produit{
#EmbeddedId
private ProduitClientPK id=new ProduitClientPK();
private Client client;
public ProduitClientPK getId() {
return id;
}
public void setId(ProduitClientPK id) {
this.id = id;
}
#JsonIgnore
#ManyToOne
#JoinColumn(name="FK_CLIENT")
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
}
class composite key :
#Embeddable
public class ProduitClientPK implements Serializable {
private long fkproduit;
private long clientSeq;
#Column(name = "FK_PRODUIT")
#Id
public long getFkProduit() {
return fkproduit;
}
public void setFkProduit(long fkproduit) {
this.fkproduit= fkproduit;
}
#Column(name = "CLIENT_SEQ")
#Id
public long getclientSeq() {
return clientSeq;
}
public void setClientSeq(long clientSeq) {
this.clientSeq= clientSeq;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PolPolAvnEntityPK that = (PolPolAvnEntityPK) o;
return fkPolice == that.fkPolice &&
avnSeq == that.avnSeq;
}
#Override
public int hashCode() {
return Objects.hash(fkPolice, avnSeq);
}
}
class client :
#Entity
public class Client {
private Long id;
private Set<Produit> produits;
#Id
#Column(name = "ID_PRODUIT")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#OneToMany(mappedBy = "client", cascade = CascadeType.ALL, orphanRemoval = true)
public Set<Produit> getProduits() {
return produits;
}
public void setProduits(Set<Produit> avenants) {
this.produits = produits;
}
public void addProduits(Produit produit){
produit.setClient(this);
produits.add(produit);
}
}
Your model does not make much sense, unless I misunderstood it. Why do you need FK_PRODUIT to be part of the primary key? If you are using a sequence for CLIENT_SEQ, this is enough to make the row unique. Apart from that, shouldn't this CLIENT_SEQ value be generated when persisting a Client? IMO you should be using something like the following:
#Entity
public class Produit{
#Id
#GeneratedValue
private Long id;
private Client client;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#JsonIgnore
#ManyToOne
#JoinColumn(name="FK_CLIENT")
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
}
I've met with the same issue not so long time ago. It would be better not to use #EmbeddedId in your case if you want one of PK fields to be generated by your database. I'm not expert in Hibernate, but as I know Hibernate doesn't try to set values to ID fields only if they are annotated by #GeneratedValue. Only this annotation can tell Hibernate to rely on database sequences. And you cannot do it in Embeddable class.
Try just use one #Id field if you want one to be generated.
Related
I've created 2 entity classes that are connected with each other. I've also made a simple service, rest controller, configuration class, unit test and method with #Scheduled annotation. Service class looks like this:
#Service
public class EntityService {
#Autowired
private Entity1Repository entity1Repository;
#Autowired
private Entity2Repository entity2Repository;
public void saveEntity1() {
entity1Repository.save(new Entity1(new Entity2("")));
entity2Repository.save(new Entity2(""));
entity1Repository.save(new Entity1(entity2Repository.findAll().get(0)));
}
}
Configuration class has method:
#Bean
public CommandLineRunner test(EntityService service) {
return args -> service.saveEntity1();
}
Rest controller, unit test and #Scheduled method are similar to the configuration, they just call saveEntity1 method from the service class.
The problem is that saveEntity1 method throws exception:
org.hibernate.PersistentObjectException: detached entity passed to persist: com.example.test.Entity2
Every time it's not called from rest controller.
After changing cascade to CascadeType.MERGE I was able to execute:
entity2Repository.save(new Entity2(""));
entity1Repository.save(new Entity1(entity2Repository.findAll().get(0)));
in EntityService from configuration etc without throwing an exception, but the line:
entity1Repository.save(new Entity1(new Entity2("")));
was still throwing exception.
My goal is to be able to use my service from rest controller, configuration, unit test etc
Here are my entity classes:
#Entity
#Table(name = "entity1")
public class Entity1 implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#JoinColumn(name = "entity2_id", referencedColumnName = "id")
#ManyToOne(optional = false, cascade = CascadeType.ALL)
private Entity2 entity2;
public Entity1() {
}
public Entity1(Entity2 ent) {
this.entity2 = ent;
}
public Entity1(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Entity2 getEntity2Id() {
return entity2;
}
public void setEntity2Id(Entity2 entity2Id) {
this.entity2 = entity2Id;
}
}
#Entity
#Table(name = "entity2")
public class Entity2 implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String str;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "entity2")
private List<Entity1> entity1List;
public Entity2() {
}
public Entity2(String str) {
this.str = str;
}
public Entity2(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public List<Entity1> getEntity1List() {
return entity1List;
}
public void setEntity1List(List<Entity1> entity1List) {
this.entity1List = entity1List;
}
}
I have a many-to-many relationship between EnfInspPrgm entity and EnfInspPmSc entity.
Here are the entity classes
public class EnfInspPrgm implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="PRM_ID")
private long prmId;
#Column(name="AREA_ID")
private long areaId;
//bi-directional many-to-one association to EnfInspPmSc
#OneToMany(mappedBy="enfInspPrgm")
private List<EnfInspPmSc> enfInspPmScs;
public EnfInspPrgm() {
}
public long getPrmId() {
return this.prmId;
}
public void setPrmId(long prmId) {
this.prmId = prmId;
}
public long getAreaId() {
return this.areaId;
}
public void setAreaId(long areaId) {
this.areaId = areaId;
}
public List<EnfInspPmSc> getEnfInspPmScs() {
return this.enfInspPmScs;
}
public void setEnfInspPmScs(List<EnfInspPmSc> enfInspPmScs) {
this.enfInspPmScs = enfInspPmScs;
}
public EnfInspPmSc addEnfInspPmSc(EnfInspPmSc enfInspPmSc) {
getEnfInspPmScs().add(enfInspPmSc);
enfInspPmSc.setEnfInspPrgm(this);
return enfInspPmSc;
}
public EnfInspPmSc removeEnfInspPmSc(EnfInspPmSc enfInspPmSc) {
getEnfInspPmScs().remove(enfInspPmSc);
enfInspPmSc.setEnfInspPrgm(null);
return enfInspPmSc;
}
}
#Entity
#Table(name="ENF_INSP_PM_SC")
public class EnfInspPmSc implements Serializable {
private static final long serialVersionUID = 1L;
//bi-directional many-to-one association to InspectionSource
#ManyToOne(optional=false)
#JoinColumn(name="ENF_INSP_SOURCE_ID")
private InspectionSource inspectionSource;
#Column(name="ENF_INSP_PRM_SRC_ID")
private long enfInspPrmSrcId;
#ManyToOne
#JoinColumn(name="PRM_ID")
private EnfInspPrgm enfInspPrgm;
public EnfInspPmSc() {
}
}
#Entity
#Table(name = "REF_ENF_INSP_SOURCE")
public class InspectionSource implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ENF_INSP_SOURCE_ID")
private Integer id;
// bi-directional many-to-one association to User
#ManyToOne
#JoinColumn(name = "CREATED_BY_USER_ID")
private User createdUser;
// bi-directional many-to-one association to User
#ManyToOne
#JoinColumn(name = "MODIFIED_BY_USER_ID")
private User modifiedUser;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public User getCreatedUser() {
return createdUser;
}
public void setCreatedUser(User createdUser) {
this.createdUser = createdUser;
}
public String getCreatedByName() {
return createdByName;
}
public void setCreatedByName(String createdByName) {
this.createdByName = createdByName;
}
public User getModifiedUser() {
return modifiedUser;
}
public void setModifiedUser(User modifiedUser) {
this.modifiedUser = modifiedUser;
}
}
I am creating a JPA repository for selecting the EnfInspPrgm entities. But itis causing an error
Invalid identifier for enfInspPrgm, ENF_INSP_SOURCE_ID
Please help me and resolve this issue.
Your code seems missing some declaration. For example EnfInspPrgm is not annotatted with #Entity.
Also, In EnfInspPmSc you didn't put getter and setter for the fields. Put getEnfInspPrgm and setEnfInspPrgm. Do the same for the other fields.
Note that JPA declartions will be translated in all cases into native sql. The owner of the relationship is always the many side which has a FK to the one side. So, If EnfInspPmSc don't have those setters and getters you won't be able to get access to any data in both tables.
I have two DTO objects say A and B which are having getters and setters and are used to take data from the database. The problem is when I am calling A, B gets called and B again points itself to A and a cycle is created.
I cannot ignore/hide the method which is creating the cycle. I need to take the whole data of A and B.
Is there any way to achieve it ?
Please help
This is my code which is causing the problem. This is application DTO which is calling environment DTO
#OneToMany(mappedBy="application", fetch=FetchType.LAZY
,cascade=CascadeType.ALL
)
public Set<EnvironmentDTO> getEnvironment() {
return environment;
}
public void setEnvironment(Set<EnvironmentDTO> environment) {
this.environment = environment;
}
And this is environment DTO which is calling the application DTO
#ManyToOne(targetEntity=ApplicationDTO.class )
#JoinColumn(name="fk_application_Id")
public ApplicationDTO getApplication() {
return application;
}
public void setApplication(ApplicationDTO application) {
this.application = application;
}
Here cycle is getting created
This is my rest call which will give result in XML format and I think while creating XML cycle is getting created
#GET
#Path("/get")
#Produces({MediaType.APPLICATION_XML})
public List<ApplicationDTO> getAllApplications(){
List<ApplicationDTO> allApplication = applicationService.getAllApplication();
return allApplication;
}
This is the Application DTO class
#Entity
#Table(name="application")
#org.hibernate.annotations.GenericGenerator(
name ="test-increment-strategy",strategy = "increment")
#XmlRootElement
public class ApplicationDTO implements Serializable {
#XmlAttribute
public Long appTypeId;
private static final long serialVersionUID = -8027722210927935073L;
private Long applicationId;
private String applicationName;
private ApplicationTypeDTO applicationType;
private String applicationDescription;
private Integer owner;
private Integer createdBy;
private Integer assignedTo;
private Date createTime;
private Date modifiedTime;
private Set<EnvironmentDTO> environment;
#Id
#GeneratedValue(generator = "test-increment-strategy")
#Column(name = "applicationId")
public Long getApplicationId() {
return applicationId;
}
private void setApplicationId(Long applicationId) {
this.applicationId = applicationId;
}
#Column(name = "applicationName")
public String getApplicationName() {
return applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
#ManyToOne(targetEntity=ApplicationTypeDTO.class
,fetch = FetchType.LAZY
)
#JoinColumn(name="applicationType")
public ApplicationTypeDTO getApplicationType() {
return applicationType;
}
public void setApplicationType(ApplicationTypeDTO applicationType) {
this.applicationType = applicationType;
}
#Column(name = "description")
public String getApplicationDescription() {
return applicationDescription;
}
public void setApplicationDescription(String applicationDescription) {
this.applicationDescription = applicationDescription;
}
#Column(name = "owner")
public Integer getOwner() {
return owner;
}
public void setOwner(Integer owner) {
this.owner = owner;
}
#Column(name = "createdBy")
public Integer getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Integer createdBy) {
this.createdBy = createdBy;
}
#Column(name = "assignedTo")
public Integer getAssignedTo() {
return assignedTo;
}
public void setAssignedTo(Integer assignedTo) {
this.assignedTo = assignedTo;
}
#Column(name = "createTime")
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
#Column(name = "modifiedTime")
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
#OneToMany(mappedBy="application", fetch=FetchType.LAZY
,cascade=CascadeType.ALL
)
public Set<EnvironmentDTO> getEnvironment() {
return environment;
}
public void setEnvironment(Set<EnvironmentDTO> environment) {
this.environment = environment;
}
This is the Environment DTO class
#Entity
#Table(name="environment")
#org.hibernate.annotations.GenericGenerator(
name = "test-increment-strategy",
strategy = "increment")
#XmlRootElement
public class EnvironmentDTO implements Serializable {
#XmlAttribute
public Long envTypeId;
#XmlAttribute
public Long appId;
private static final long serialVersionUID = -2756426996796369998L;
private Long environmentId;
private String environmentName;
private EnvironmentTypeDTO environmentType;
private Integer owner;
private Date createTime;
private Set<InstanceDTO> instances;
private ApplicationDTO application;
#Id
#GeneratedValue(generator = "test-increment-strategy")
#Column(name = "envId")
public Long getEnvironmentId() {
return environmentId;
}
private void setEnvironmentId(Long environmentId) {
this.environmentId = environmentId;
}
#Column(name = "envName")
public String getEnvironmentName() {
return environmentName;
}
public void setEnvironmentName(String environmentName) {
this.environmentName = environmentName;
}
#ManyToOne(targetEntity=EnvironmentTypeDTO.class)
#JoinColumn(name = "envType")
public EnvironmentTypeDTO getEnvironmentType() {
return environmentType;
}
public void setEnvironmentType(EnvironmentTypeDTO environmentType) {
this.environmentType = environmentType;
}
#Column(name = "owner")
public Integer getOwner() {
return owner;
}
public void setOwner(Integer owner) {
this.owner = owner;
}
#Temporal(TemporalType.DATE)
#Column(name = "createTime")
public Date getCreateTime()
{
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
#OneToMany(mappedBy="environment", cascade=CascadeType.ALL, fetch = FetchType.EAGER)
public Set<InstanceDTO> getInstances() {
return instances;
}
public void setInstances(Set<InstanceDTO> instances) {
this.instances = instances;
}
#ManyToOne(targetEntity=ApplicationDTO.class )
#JoinColumn(name="fk_application_Id")
//#XmlTransient
public ApplicationDTO getApplication() {
return application;
}
public void setApplication(ApplicationDTO application) {
this.application = application;
}
Your object graph is cyclic. There is nothing intrinsically wrong with that, and it is a natural consequence of using JPA.
Your problem is not that your object graph is cyclic, but that you are encoding it in a format which cannot handle cycles. This isn't a Hibernate question, it's a JAXB question.
My suggestion would be to stop JAXB from attempting to marshal the application property of the EnvironmentDTO class. Without that property the cyclic graph becomes a tree. You can do this by annotating that property with #XmlTransient.
(confession: i learned about this annotation by reading a blog post by Mr Doughan, which i came across after reading his answer to this question!)
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
MOXy offers the #XmlInverseReference extension to handle this use case. Below is an example of how to apply this mapping on two entities with a bidirectional relationship.
Customer
import javax.persistence.*;
#Entity
public class Customer {
#Id
private long id;
#OneToOne(mappedBy="customer", cascade={CascadeType.ALL})
private Address address;
}
Address
import javax.persistence.*;
import org.eclipse.persistence.oxm.annotations.*;
#Entity
public class Address implements Serializable {
#Id
private long id;
#OneToOne
#JoinColumn(name="ID")
#MapsId
#XmlInverseReference(mappedBy="address")
private Customer customer;
}
For More Information
http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html
http://blog.bdoughan.com/2013/03/moxys-xmlinversereference-is-now-truly.html
My advice is not exposing your JPA entity class to your webservices. You can create different POJO class and convert your JPA entity to the POJO. For example:
this is your JPA entity
import javax.persistence.*;
#Entity
public class Customer {
#Id
private long id;
#OneToOne(mappedBy="customer", cascade={CascadeType.ALL})
private Address address;
}
you should use this class for your webservices:
public class CustomerModel{
private long id;
//you can call different WS to get the Address class, or combine to this model
public void setFromJpa(Customer customer){
this.id = customer.id;
}
}
I am trying to implement a tagging system in my database (MySQL V5.1.61), and then get that working in hibernate. Here are the relevant parts of my database:
And the data contained:
If I am doing this correctly, then 'nir' should have 3 tags associated with him, 'Food','Sorority', and 'Summer Internship'.
What I am having trouble with is implementing this relationship in hibernate (Using annotations):
The UserHibernate class (I'm using GWT so I need separate hibernate and DTO objects):
#Entity
#Table(name="user")
public class UserHibernate implements Serializable{
private int ID;
//removed fields for brevity
private Set<UserTagsHibernate> tags;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "uID")
public int getID() {
return ID;
}
public void setID(int iD) {
ID = iD;
}
/**
* #return the tags
*/
#OneToMany(mappedBy="user", fetch=FetchType.EAGER)
public Set<UserTagsHibernate> getTags() {
return tags;
}
/**
* #param tags the tags to set
*/
public void setTags(Set<UserTagsHibernate> tags) {
this.tags = tags;
}
}
The UserTagsHibernate Class:
#Entity
#Table(name="usertags")
public class UserTagsHibernate {
private int usertagsID;
private UserHibernate user;
private TagsHibernate tags;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="userforeignkey")
public UserHibernate getUser() {
return user;
}
public void setUser(UserHibernate userHibernate) {
this.user = userHibernate;
}
#OneToOne
#JoinColumn(name="tagforeignkey")
public TagsHibernate getTags() {
return tags;
}
public void setTags(TagsHibernate tagsHibernate) {
this.tags = tagsHibernate;
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "usertagsID")
public int getUsertagsID() {
return usertagsID;
}
public void setUsertagsID(int usertagsID) {
this.usertagsID = usertagsID;
}
}
The TagsHibernate Class:
#Entity
#Table(name = "tags")
public class TagsHibernate {
private int tagID;
//removed for brevity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "tagID")
public int getTagID() {
return tagID;
}
public void setTagID(int tagID) {
this.tagID = tagID;
}
}
The problem that I am having is that when I try and retrieve a user, here 'nir', he shows up three times. I believe it is because he has 3 tags, so for some reason, when I issue the query "session.createCriteria(UserHibernate.class).add(Restrictions.eq("username", "nir")).list();" I get a list of length 3. Any ideas why this is happening?
This problem pops up all the time when using the Criteria API...it's a known quirk. The workarounds are either to use HQL instead or to add a transformer that filters out the duplicates like so:
session.createCriteria(UserHibernate.class)
.add(Restrictions.eq("username", "nir"))
.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
.list();
An entity X has a list of entity Y and the entity Y has an instance of entity Z.
The relation between X to Y is OneToMany and the relation between Y to Z is ManyToOne.
I want to retrieve X and have all the associated entities retrieved with them as well.
What HQL query do I write so that I get the whole chain retrieved all at once. At present its hibernateTemplate.find("from X").
or What annonations do I use for it?
X=ServiceProvider, Y=BusinessLocations.java, Z=State.java
I have the entities annotated below and I am having the whole chain persisted into database but when i try to retrieve the list of Y(BusinessLocation), I get
nothing.
What do I do join X with Y and Y with Z?
Below are the entities x, Y and Z.
ServiceProvider.java
#Entity
public class ServiceProvider implements Serializable{
private Long id;
private Set<BusinessLocation> businessLocations = new HashSet<BusinessLocation>();
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#OneToMany(mappedBy="serviceProvider", targetEntity=BusinessLocation.class, cascade=CascadeType.ALL, fetch=FetchType.EAGER)
public Set<BusinessLocation> getBusinessLocations() {
return businessLocations;
}
public void setBusinessLocations(Set<BusinessLocation> businessLocations) {
this.businessLocations = businessLocations;
}
public boolean equals(Object obj) {
if(!(obj instanceof ServiceProvider)) return false;
ServiceProvider other = (ServiceProvider) obj;
return new EqualsBuilder().append(businessLocations, other.businessLocations).isEquals();
}
}
BusinessLocation.java
#Entity
public class BusinessLocation implements Serializable{
private Long id;
private String address;
private String city;
private State state;
private String pincode;
private ServiceProvider serviceProvider;
public BusinessLocation() {
}
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="state_id")
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public void setPincode(String pincode) {
this.pincode = pincode;
}
public String getPincode() {
return pincode;
}
#ManyToOne
#JoinColumn(name="serviceProvider_id")
public ServiceProvider getServiceProvider() {
return serviceProvider;
}
public void setServiceProvider(ServiceProvider serviceProvider) {
this.serviceProvider = serviceProvider;
}
public boolean equals(Object obj) {
if( !(obj instanceof BusinessLocation)) return false;
BusinessLocation other = (BusinessLocation) obj;
return new EqualsBuilder().append(address, other.address).append(city, other.city).append(state, other.state).append(pincode,
other.pincode).append(serviceProvider, other.serviceProvider).isEquals();
}
public int hashCode() {
return new HashCodeBuilder().append(address).append(city).append(state).append(pincode).append(serviceProvider).toHashCode();
}
}
State.java
#Entity
public class State implements Serializable {
private Long id;
private String abbreviatedName;
private String name;
private List<BusinessLocation> businessLocations;
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getAbbreviatedName() {
return abbreviatedName;
}
public void setAbbreviatedName(String abbreviatedName) {
this.abbreviatedName = abbreviatedName;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
#OneToMany(mappedBy="state", targetEntity=BusinessLocation.class, cascade=CascadeType.ALL, fetch=FetchType.EAGER)
public List<BusinessLocation> getBusinessLocations() {
return businessLocations;
}
public void setBusinessLocations(List<BusinessLocation> businessLocations) {
this.businessLocations = businessLocations;
}
public boolean equals(Object obj) {
if(! (obj instanceof State)) return false;
State other = (State) obj;
return new EqualsBuilder().append(abbreviatedName, other.abbreviatedName).append(name, other.name).append(businessLocations,
other.businessLocations).isEquals();
}
public int hashCode() {
return new HashCodeBuilder().append(name).append(abbreviatedName).append(businessLocations).toHashCode();
}
}
Could someone help me out here?
Thanks
What about:
Query q - entityManager.createQuery("Select x from ServiceProvider x inner join x.businessLocations as y and inner join y.state as z where x.id = ?1");
I have the entities annotated below and I am having the whole chain persisted into database but when i try to retrieve the list of Y (BusinessLocation), I get... nothing
You should activate SQL logging to see what is happening and check the data because the annotation part looks correct:
the one-to-many between ServiceProvider and BusinessLocation is EAGER
the many-to-one between BusinessLocation and State is EAGER (by default)
So the whole chain should be retrieved eagerly. If this is not what is happening, you might want to check the data and the SQL, hence the suggestion.
As an alternative to EAGER associations, you could use a FETCH JOIN to prefetch the related data:
FROM ServiceProvider provider
INNER JOIN FETCH provider.businessLocations location
LEFT JOIN FETCH location.state
But note that JPA 1.0 does not allow nested join fetches in JPQL, this is Hibernate specific.
References
Hibernate Core Reference Guide
14.3. Associations and joins
JPA 1.0 specification
Section 9.1.22 "ManyToOne Annotation"
Section 4.4.5.3 "Fetch Joins"