i have a table with 6 columns and one of them is a CLOB column, during search against this table i don't want to bring this column data but when user requests for the details then ill load it, how do i achieve this with Spring Data JPA, I try to use Projections + MetaModel Object with no help
I did this by moving two columns into a separate table, but even if you can't touch the database, you can always map two different entities to different columns of the same table.
Heavyweight child entity:
#Entity
public class RawImage implements Serializable {
// other properties
private String type;
private byte[] data;
}
Lightweight parent entity:
#Entity
public class Model implements Serializable {
// other properties (ID etc.)
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "fk_image_id")
private RawImage image;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "fk_thumb_id")
private RawImage thumb;
}
This means the image/thumb data isn't loaded by default, and I just added a fetch when I do need them with something like the following:
String jpql = "select m from Model m join fetch m.image i";
There are other techniques described in this answer but they weren't as clean as I think this simple solution is.
Related
I have a Spring Boot application using Hibernate as JPA provider. My application has two entities connected with a #OneToMany / #ManyToOne relation. The relation is annotated with #Fetch(FetchMode.JOIN) on both directions, and fetch = FetchType.EAGER.
My entities are called Car and Driver:
#Entity
#Table(name = "car")
#Data
public class Car implements Serializable, Cloneable {
#Id
#GenericGenerator(name = "car_seq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = {
#Parameter(name = "sequence_name", value = "car_seq") })
#GeneratedValue(generator = "car_seq")
private Integer id;
#OneToMany(mappedBy = "car", fetch = FetchType.EAGER)
#Fetch(FetchMode.JOIN)
private List<Driver> drivers = new ArrayList<>();
#Column(name = "license_no", nullable = false)
private String licenseNo;
}
#Entity
#Table(name = "driver")
#Data
public class Driver implements Serializable, Cloneable {
#Id
#GenericGenerator(name = "driver_seq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = {
#Parameter(name = "sequence_name", value = "driver_seq") })
#GeneratedValue(generator = "driver_seq")
private Integer id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "car_id", nullable = true)
#Fetch(FetchMode.JOIN)
private Car car;
#Column(name = "name", nullable = false)
private String name;
}
When selecting a care (e.g. by calling Car.findById()), Hibernate joins the the two tables in a single SQL, and returns a Car object with a list of Drivers.
But if I select a single driver, Hibernate will join the Driver and Car table to give me the Driver object with the Car property populated, but it will run a second query to fetch all the driver objects on for the list on the car object.
For performance reasons I would like all the involved objects to be fetched in a single query, as is the case when I fetch a car. But I cannot find a way to make Hibernate do this. There is a property, hibernate.max_fetch_depth, which is supposed to do this, but I have found that it only affects the behavior of fetching a car, not when I fetch a driver.
I know I can use an EntityGraph to control the fetching, and by using an EntityGraph I have successfully retrieved a driver object with its car and all the car's drivers in one query. But to do that, I have to explicitly use a graph when retrieving the object, and I cannot do that in all the various cases where a Car object is needed. There are lots of other entities that have a relation to Car, and I don't want to write an EntityGraph for each and every one of those.
So is there a way to tell Hibernate how you want the fetching to be done by default on an entity? I would have thought that the annotations would be enough, but it seems that there either has to be something more, or that this simply cannot be done.
Arndt
FetchType.EAGER is one of the most common reasons for performance problems. You should use
#OneToMany(mappedBy = "car")
private List<Driver> drivers = new ArrayList<>();
And fetch drivers If needed
SELECT c FROM Car c JOIN FETCH c.drivers
I have a Project and Employee entities, which has ManyToMany relationship like below.
#Entity
public class Project {
#Id #GeneratedValue
private int projectId;
private String projectName;
// has some additional columns
#ManyToMany(mappedBy = "projects")
private List<Employee> emp = new ArrayList<Employee> ();
....
.....
}
#Entity
public class Employee {
#Id #GeneratedValue
private int id;
private String firstName;
private String lastName;
#ManyToMany(cascade=CascadeType.ALL)
List<Project> projects = new ArrayList<Project> ();
....
....
}
When I use above entities, JPA create a mpping table 'Employee_Project' like below.
create table Employee_Project (emp_id integer not null, projects_projectId integer not null)
My question is, whenever new employee is added, I want to update both employee table and Employee_Project mapping table only, assume I know project id that I would like to map this employee to. (without touching project table/entity, I mean why should I provide complete project object, while saving employee entity alone, how can I do this via jpa?)
You don't need to provide the entire Project object. Use EntityManager.getReference(projectId) or JpaRepository.getOne(projectId).
Those methods will create a proxy object with the appropriate id, rather than loading the entire Project entity from the data store.
EDIT Your service method should look pretty much like the following:
#Transactional
public void createEmployee(Employee employee, Long projectId) {
employee.setProjects(List.of(projectRepository.getOne(projectId));
employeeRepository.save(employee);
}
As a side note, CascadeType.ALL (in particular, because it includes CascadeType.MERGE and CascadeType.REMOVE) doesn't make sense for #ManyToMany. Unless you're planning to create a Project by creating an Employee, CascadeType.PERSIST makes no sense, either.
Here is my data structure.
#Entity
public class JobEntity {
#Id
private Long id;
private String name;
#OneToMany(fetch = FetchType.EAGER,cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, mappedBy = "parentJob")
private List<JobEntity> subJobs;
#ManyToOne
#JoinColumn(name = "parent_job")
private JobEntity parentJob;
}
So the job entity has a tree data structure, If I want to add pessimistic lock like below, using JPA:
Map<String, Object> properties = new HashMap<>();
map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED);
entityManager.find(JobEntity.class, 1L,LockModeType.PESSIMISTIC_WRITE, properties);
Would the pessimistic lock work on all referenced sub rows in the tree data structure?
Or just the row with id "1L"? Or just the rows directly referenced from "1L"?
It should work but note that if you are using Hibernate, unfortunately it's not yet supported
The javax.persistence.lock.scope is not yet supported as specified by the JPA standard.
From Hibernate Docs
Related Ticket
I want to know if attributes such as Int or Varchar2 are fetched eagerly or lazily when creating a regular query.
#Entity
#Table(name = "ROOM")
public class Room implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "room_id")
private Integer id;
#Column(name = "number")
private String number;
#Column(name = "capacity")
private Integer capacity; //Will this be fetched eagerly???
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "building_id")
private Building building;
...
...
...
}
Basically:
Room room = getRoomById(5); // select room from Room room where room.id = 5;
Integer roomCap = room.getCapacity(); //Will this create another query?
Or will it be inside the room object already?
The fetching strategies eager or lazy apply to relationships or associated entities where you want to load the data from another table and not to the columns that are part of the same entity or table. Columns or fields that belong to the same entity or table are fetched when you retrieve the entity and no separate query is fired.
when use lazy loading after data call on db after that you want to this object inside object your query go to db and execute db.
When you eager attribute get All element thats object
i have an existing table for TransactionLogs which is either links to a External or to a InternalType. the id's corresponding to the cash adjustment & game transaction are stored in a single column called transaction id and a separate column called type indicates which table is it linked to
Because of the nature of the existing table, i mapped it in a single table inheritance:
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.INTEGER)
public class TransLog implements Serializable {
#Id
#GeneratedValue
private Long id;
private Integer type;
// getters and setters
}
#Entity
public class InternalAdjustmentTransLog extends TransLog {
#ManyToOne
#JoinColumn(name = "TransID", nullable = false)
private InternalAdjustmentRecord internalAdjustmentRecord;
// getters and setters
}
#Entity
public class ExternalTransLog extends TransLog {
#ManyToOne
#JoinColumn(name = "TransID", nullable = false)
private ExternalAdjustmentRecord externalAdjustmentRecord;
}
each of these two subclasses has their subclasses with defined descriminator values..
With the setup given above, there are instances that i need to get a unified data of both
internal and external records. What is the best way to accomplish this? at first i thought it would be enough to use the TransLog as the root class for the query (i'm using jpa criteria). however, i need to get TransId (which are defined in the subclasses and points to 2 different objects of no relationship).
Thanks.
You can make abstract method in TransLog that returns what you need and implement it in both subclasses.