I am trying to model a weather service that supports multiple locations. My application has 3 entities:
Location - information about the Location
Weather (includes location id) - A single hour of weather data
LocationWeather - Subclass of Location containing a List of Weather
I want Location to sometimes exist as a distinct entity from Weather. That way I can expose CRUD operations on the Location object without carrying the bloat of all of its weather data. However, I still want to satisfy the primary use case of returning weather for a given location, that's why LocationWeather exists.
Can what I want to do be done with inheritance? I've tried
#Entity
#Table(name="location")
#Inheritance(strategy = InheritanceType.JOINED)
public class Location {
but my subclass (LocationWeather) doesn't directly associate with a table. Should I move my List of Weather up to the Location object and somehow mark it as optional?
Location.java
#Entity
#Table(name="location")
public class Location {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="location_id")
private int id;
#Column(name="name")
private String name;
Weather.java
#Entity
#IdClass(WeatherId.class)
#Table(name="weather")
public class Weather {
#Id
#Column(name="location_id")
private int locationId;
#Id
#Column(name="time")
private Date time;
#Column(name="temperature")
private Double temperature;
LocationWeather.java
#Entity
public class LocationWeather extends Location{
#ElementCollection
#CollectionTable(name="weather", joinColumns= {#JoinColumn(name="location_id")})
#Column(name="weather")
private List<Weather> weather;
Figured it out. I was googling the wrong things. I should have been searching for "2 entities 1 table"
I was able to solve the issue by creating a #MappedSuperclass and creating Location and LocationWeather as a subclass of it.
Now I have:
MappedLocation.java
#MappedSuperclass
public class MappedLocation {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="location_id")
private int id;
#Column(name="name")
private String name;
Location.java
#Entity
#Table(name="location")
public class Location extends MappedLocation {
}
LocationWeather.java
#Entity
public class LocationWeather extends MappedLocation{
#ElementCollection
#CollectionTable(name="weather", joinColumns= {#JoinColumn(name="location_id")})
#Column(name="weather")
private List<Weather> weather;
More info: https://thoughts-on-java.org/hibernate-tips-map-multiple-entities-same-table/
Related
Let's say I have two entities:
#Entity
public class Phone {
#Id
private Long id;
private String number;
}
#Entity
public class Person {
#Id
private Long id;
private String name;
}
The relationship between a person and a phone is one to one.
How could I access only the phone's number in the Person entity mapped by the phone's id
#Entity
public class Person {
#Id
private Long id;
private String name;
// ???
private String phoneNumber;
}
The reason for not mapping the whole entity is because in some more realistic entities there are too many properties.
I don't think you can, but something like this might be acceptable:
public class Person {
#OneToOne
#JoinColumn(name = "phone_id")
private Phone phone;
public String getPhoneNumber() {
return phone.getNumber();
}
}
Although you have mapped the whole object, not just the single property, you have only exposed the single property you want. The other stuff is hidden.
Alternatively, do it at the DB layer using a View:
create view person_with_phone as
select p.id, p.name,f.number
from person p
join phone f on f.id=p.phone_id
and then have an entity class to match the view. You'll need to turn off schema creation in your JPA implementation.
I use h2 in memory db and I don't want to create duplicate locations in my DataBase. Only when I use createItem and input location column id manualy it write it to the same location. Otherwise even if the country city gps coordinates are the same app write it to other location with it's id.
I tried to understand but It's not working
I got these entities.
#Entity
#Table(name = "item_System_items")
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
private String description;
private BigDecimal price;
private Integer stock;
#ManyToOne
#JoinColumn(name = "location_id")
#Cascade(value={org.hibernate.annotations.CascadeType.ALL})
private Location location;
And
#Entity
#Table(name = "item_System_locations")
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String country;
private String city;
private String street;
private String gpsCoordinates;
SETTERS AND GETTERS IS THERE I JUST NOT POST THEM HERE
Controller
#RestController
#RequestMapping("/items")
public class ItemsController {
#Autowired
private ItemsService service;
#PostMapping
#ResponseStatus(value=HttpStatus.CREATED)
public int createItem(#RequestBody Item item) {
return service.createItem(item);
}
Service
#Service
#Transactional
public class ItemsService {
#Autowired
private ItemJPARepository repository;
public int createItem(Item item) {
return repository.save(item).getId();
}
I expect after re-coding app doesn't make new location if the column values are the same.
Thank you people!
If you really help me I would be so happy!
There is nothing in your Entity definitions to tell the ORM about the constraint you want.
You can add #UniqueConstraint to the #Table in your Location entity and specify which column(s) must all be in a unique combination (example given in the linked documentation):
#Table(
name="EMPLOYEE",
uniqueConstraints=
#UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})
)
This will add a check in the database, if the ORM is managing your database schema, which will throw an exception when violated.
I am using Spring Boot 1.3.7.RELEASE as application framework, Hibernate as JPA implementation, Spring Data as data access interface, and MySQL 5.7.15 as storage server. I have two models: Agency and ServiceAreaCoverage. Agency has oneToOne unidirectional with ServiceAreaCoverage at the moment. Here's my application model's and mapping's.
#Entity
#Table(name = "agencies")
public class Agency {
#Id
private String id;
private String name;
private long phoneNumber;
private String email;
private String websiteUrl;
private boolean active;
#OneToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "service_area_coverage_id")
private ServiceAreaCoverage serviceAreaCoverage;
}
#Entity
#Table(name = "service_area_coverages")
public class ServiceAreaCoverage {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private double latitude;
private double longitude;
private double latitudeSpan;
private double longitudeSpan;
}
I am trying to save Agency with ServiceAreaCoverage using Spring Data JpaRepository's save method. Agency and ServiceAreaCoverage both get saved and key constraints are also properly managed. However, double values are not getting saved. Instead of double values, zero is getting saved. I cannot think of any reason for this result to happen nor I can think of troubleshooting idea's. So any help would really be appreciated guys. Here's my some other codes and references relative to this matter.
Service Code:
#Service
public class AgencyServiceImpl implements AgencyService {
#Autowired
private AgencyRepository agencyRepository;
#Override
public void createAgency(Agency agency) {
this.agencyRepository.save(agency);
}
}
Repository Interface Code:
#Repository
public interface AgencyRepository extends JpaRepository<Agency, String> {
}
Generated Table Details:
Agencies Table:
ServiceAreaCoverage Table:
I think this may help you.
You should use wrapper classes instead of primitives.
Please change double to Double in your ServiceAreaCoverage class as it can hold even null value. Make sure the getter/setters are in Double instead of double.
#Entity
#Table(name = "service_area_coverages")
public class ServiceAreaCoverage {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Double latitude;
private Double longitude;
private Double latitudeSpan;
private Double longitudeSpan;
}
Note: You can not use the primitive double when the DB column is set as not-null="true".
Always use wrapper classes, so you don't have to think for 0 or null.
I saw similar questions, but answers weren't helpful. So, i get this error:
Use of #OneToMany or #ManyToMany targeting an unmapped class: com.podro.model.Journey.roadWay[com.podro.model.RoadElement]
I'm trying to create List with objects of RoadElements (which is interface for class Point and Section). There is any other way to do it? From what i know, i guess that is the only way to create proper mapping for this classes, and have list of this elements.
#Entity
#Table(name="Journey")
public class Journey {
// Some other fields
#Column(name="road_way")
#ManyToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
private List<RoadElement> roadWay;
}
#MappedSuperclass
public interface RoadElement {}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name="Point")
public class Point implements RoadElement{
#Id
#Column(name="id")
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
private String name;
#Column(name="time_in_days")
private int timeInDays;
private Rate rating;
}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name="Section")
public class Section implements RoadElement{
#Id
#Column(name="id")
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name="section_name" , length=100)
private String sectionName;
#Column(name="time_in_days")
private int timeInDays;
#Column(name="kind_of_transport")
private Locomotion kindOfTransport;
}
Thanks for answers, I would be very grateful for help!
Associations are between entities. RoadElement is not an entity. It's an interface.
You may not do what you're trying to do. Hibernate needs to know the type of the entities contained in roadWay.
So, RoadElement should be a class, annotated with #Entity, having an ID that uniquely identifies a RoadElement among all the road elements (sections, points, etc.)
Section and Point should extend from RoadElement, and should NOT have their own ID, since it's inherited from RoadElement.
I have a class that looks something like this:
#Entity
public class EdgeInnovation {
#Id
public long id;
#ManyToOne
public NodeInnovation destination;
#ManyToOne
public NodeInnovation origin;
}
and another one that looks something like this:
#Entity
public class NodeInnovation {
#Id
public long id;
#OneToOne
public EdgeInnovation replacedEdge;
}
and so each table map to the other, so one entity will refer to other entities that will refer to more entities and so on, so that in the end there will be many entities that will be fetched from the database. Is there any way to only get the value (integer/long) of the key and not the entity it refers to? something like this:
#ManyToOne(referToThisTable="NodeInnovation")
#Entity
public class EdgeInnovation {
#Id
public long id;
#ManyToOne(referToTable="NodeInnovation")
public Long destination;
#ManyToOne(referToTable="NodeInnovation")
public Long origin;
}
and
#Entity
public class NodeInnovation {
#Id
public long id;
#OneToOne(referToTable="EdgeInnovation")
public Long replacedEdge;
}
Here's an example. I want the stuff in green, I get all the stuff in red along with it. This wastes memory and time reading from disk.
You would just map the foreign keys as basic mappings instead of Relationships:
#Entity
public class EdgeInnovation {
#Id
public long id;
#Column(name="DESTINATION_ID")
public Long destination;
#Column(name="ORIGIN_ID")
public Long origin;
}
Or you can have access to both the ID and the referenced entity within EdgeInnovation, but you'll need to decide which you want to use to set the mapping:
#Entity
public class EdgeInnovation {
#Id
public long id;
#Column(name="DESTINATION_ID", updatable=false, insertable=false)
public Long destination_id;
#ManyToOne
public NodeInnovation destination;
#Column(name="ORIGIN_ID", updatable=false, insertable=false)
public Long origin_id;
#ManyToOne
public NodeInnovation origin;
}
In the above example, the origin_id is read-only while the origin reference is used to set the foreign key in the table. Any changes though should be made to both fields to keep the object mappings in synch with each other.
Another alternative is to use the provider's native code to find if the reference is lazy and wasn't triggered, and then get the foreign key value. If it has been triggered, you can just use the reference to get the ID value, since it won't cause a query to fetch anything. This is something you would have to look into EclipseLink's source code for though.
Sorry, I cant comment so I put it here ,
I think it should be like that
#Entity
public class EdgeInnovation {
#Id
public long id;
#ManyToOne
public NodeInnovation destination;
#ManyToOne
public NodeInnovation origin;
}
And the other class is :
#Entity
public class NodeInnovation {
#Id
public long id;
#OneToMany(mappedBy="origin")
public List<EdgeInnovation> replacedEdges;
}
If I'm getting the situation wrong sorry, (Could you draw your classes with the relations so I can get it straight?)
Why not use a new construction in JPA and a custom constructor in NodeInnovation? Basically, create a transient property in NodeInnovation for use when you only want the EdgeInnovation id:
#Entity
public class NodeInnovation {
#Id #GeneratedValue private Long id;
private Integer type;
#OneToOne
private EdgeInnovation replacedEdge;
#Transient
private Long replacedEdgeId;
public NodeInnovation() {}
public NodeInnovation(Long id, Integer type, Long replacedEdgeId ) {
this.id = id;
this.type = type;
this.replacedEdgeId = replacedEdgeId;
}
...
}
Use it like so:
NodeInnovation n = em.createQuery("select new NodeInnovation(n.id, n.type, n.replacedEdge.id) from NodeInnovation n where n.id = 20", NodeInnovation.class).getSingleResult();
You didn't say how you were selecting NodeInnovation, whether directly or through a join, but either way the trick is the new NodeInnovation in the JPQL or CriteriaBuilder query.
I am aware I am quite late but some people might look for an answer to the same question - in your JPA repository you could do something like this:
#Query("SELECT new java.lang.Integer(model.id) FROM #{#entityName} model WHERE model.relationModeFieldlName.id IN :relationModelIds")
List<Integer> findIdByRelationModelIdIn(#Param("relationModelIds") List<Long> relationModelIds);