1

I'm having some trouble getting an attribute on an entity to lazy load. I've scoured the EclipseLink documentation but can't figure out what I'm doing wrong.

Basically, I have a table with some columns, one of which is a file, the size of which can be very large. I'd like to have the file itself lazy load, so that when I load all the rows of the table I don't have to wait for the file to load into memory if it won't be used.

The setup:

EclipseLink 2.6.2

ReportHistoryFile:

@Entity
@Table(name = "REPORT_HISTORY_FILES")
@NamedQueries({
    @NamedQuery(name = "ReportHistoryFile.findAll", query = "SELECT p FROM ReportHistoryFile p order by p.createDate DESC")})
public class ReportHistoryFile implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @SequenceGenerator(name = "SEQ_REPORTHISTORYFILE_GEN", sequenceName = "SEQ_REPORTHISTORYFILE", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_REPORTHISTORYFILE_GEN")
    private Long fileId;

    // note the basic lazy setting
    @Basic(fetch = FetchType.LAZY)
    @Column(name = "FILE_CONTENT", nullable = false)
    private byte[] fileContent;
    @Column(name = "FILE_NAME", nullable = false)
    private String fileName;
    private String contentType;
    @Column(name = "FILE_SIZE")
    private Long fileSize;
    @Column(name = "CREATEDATE")
    private Date createDate;

    public ReportHistoryFile() {
    }

    public Long getFileId() {
        return fileId;
    }

    public void setFileId(Long fileId) {
        this.fileId = fileId;
    }

    public byte[] getFileContent() {
        if (fileContent == null) {
            return null;
        }
        return Arrays.copyOf(fileContent, fileContent.length);
    }

    public void setFileContent(byte[] fileContentIn) {
        if (fileContentIn != null) {
            this.fileContent = Arrays.copyOf(fileContentIn, fileContentIn.length);
        }
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public String getContentType() {
        return contentType;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

    public Long getFileSize() {
        return fileSize;
    }

    public void setFileSize(Long fileSize) {
        this.fileSize = fileSize;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public Date getCreateDate() {
        return this.createDate;
    }
}

But when I execute this code (em is the EntityManager):

    List<ReportHistoryFile> list = null;
    try {
        list = (List<ReportHistoryFile>) em.createNamedQuery("ReportHistoryFile.findAll")
                .getResultList();
    } catch (javax.persistence.NoResultException e) {
        log.info("No Report History File records found.");
    } catch (Exception e) {
        log.error(e.getMessage(), e);
    }

I see this printed out to the console:

[EL Fine]: sql: 2017-01-25 10:35:44.223--ServerSession(1159663071)--Connection(39105078)--SELECT FILEID, CONTENTTYPE, CREATEDATE, FILE_CONTENT, FILE_NAME, FILE_SIZE FROM REPORT_HISTORY_FILES ORDER BY CREATEDATE DESC

Notice that FILE_CONTENT is still in the statement. What am I missing? I don't think I need anything special in my persistence.xml file for just the basic lazy load, but I could be wrong. Thanks in advance!

lucasvw
  • 1,345
  • 17
  • 36
  • this might help - http://stackoverflow.com/questions/2112508/basicfetch-fetchtype-lazy-does-not-work – Maciej Kowalski Jan 25 '17 at 17:05
  • @MaciejKowalski That has to do with Hibernate, not EclipseLink, so I'm not sure that is applicable. Thanks though! – lucasvw Jan 25 '17 at 18:24
  • Sadly the JPA spec treats the LAZY/EAGER setting as a "hint" to the JPA provider. The JPA provider I use (DataNucleus) takes what I tell it and doesn't override my wishes, whereas yours (EclipseLink) thinks it knows best so pulls it back always. Don't think there is a way around that (other than switching provider!!) ... try an EntityGraph and see if they see sense when using that? – Neil Stockton Jan 25 '17 at 18:42
  • Neil, I'm sure you are fully aware that basics are lazy loaded by most providers under certain circumstances, just as OneToOnes are, which is why they were left as hints in the JPA spec. EclipseLink requires the use of byte code enhancements to the java classes (weaving) so that they can be instrumented to support lazy basics and OneToOne mappings. You don't need to switch providers, just look at https://www.eclipse.org/eclipselink/documentation/2.5/concepts/app_dev007.htm – Chris Jan 26 '17 at 15:59
  • Chris, I'm well aware that I use one implementation which does what the person is asking for and I HEAR people saying that their implementation isn't doing something so I answer based on facts that I know. Perhaps EclipseLink could give a suitable message to the user if they haven't enhanced the class and they are requesting something that needs it (or maybe it already does). Either way there is room for an implementation to do something better ... – Neil Stockton Jan 26 '17 at 18:04
  • EclipseLink does say why a lazy or other option might be ignored when weaving isn't used - but you must enable logging and of course subtle messages are easily ignored if you don't know what you are looking for. I didn't mean to come across as insulting - I just was surprised by your first comment being to switch providers; I don't expect you to know how Hibernate, EclipseLink or OpenJPA handle lasy attributes, but the quality of your comments and answers just had me assuming you would. – Chris Jan 30 '17 at 15:30

1 Answers1

0

In order to enable lazy loading feature for properties and relations, your classes must be weaved by eclipseLink using

  • static weaving
  • dynamic weaving

How to do this you can read here, working example can be found here

slavik
  • 1,223
  • 15
  • 17