T
tuxedo
Gast
Hallo,
hab ein kleines Problem mit unserem Server in Verbindung mit einer JPA Query. Ich versuchs mal möglichst klar darzustellen:
Ich habe folgende Entity:
Und ich habe eine weitere Entity, die von der ersten erbt:
Und eine Abfragelogik die wie folgt ausschaut:
Hintergrund der Sache:
Über die Datenbank ist ein kleines aber feines Dateisystem realisiert. Ich bin mir bewusst, dass das nicht der optimale Weg ist eine Datei auf dem Server abzulegen. Aber in diesem Fall gings nicht anders ohne einen extremen Aufwand zu bewältigen.
In der Datenbank (PostgreSQL 8.3) liegen rund 600 Einträge. Manche davon sind "Verzeichnisse" und haben somit keine Binärdaten, andere wiederum sind Dateien und haben Binärdaten mit bis zu 10MB Größe.
Die oben gezeigte Methode soll den Inhalt eines Verzeichnisses auflisten OHNE die Binärdaten mit abzufragen.
In einem Verzeichnis das 83 Dateien mit einer Gesamtgröße von etwa 100MB hat dauert die Abfrage auf unserem System um die 10sec und die CPU schnellt auf 80-100%
Mache ich die Abfrage auf ein Verzeichnis das weniger Datein hat (und somit auch weniger Gesamt-MB), dann geht die Abfrage rasend schnell und die CPU ist davon wenig beeindruckt.
Kann mir jemand sagen - mal abgesehen davon dass man Dateien nicht in einer DB speichern sollte - was hier falsch läuft? Hab ich die Entity falsch aufgebaut? Ist die Abfrage schlecht gewählt?
Meine Vermuting ist dass es mit der Vererbung der Entity Probleme gibt und eine Abfrage auf "nur die Meta-Daten" auch noch die Binärdaten irgendwie mit einbezieht ...
Gruß
Alex
hab ein kleines Problem mit unserem Server in Verbindung mit einer JPA Query. Ich versuchs mal möglichst klar darzustellen:
Ich habe folgende Entity:
Java:
@Entity
@Table(name = "cms_medc_files")
public class DBFileMetaData
implements Serializable
{
/**
* object is a DIRECTORY
*/
public static final int TYPE_DIR = 0;
/**
* object is a FILE
*/
public static final int TYPE_FILE = 1;
private static final long serialVersionUID = 2L;
private long mId;
private String mName;
private String mPath;
private int mType;
private int mSize;
private Date lastModified;
/**
* Returns the db internal ID of this entry.
* Normally this isn't used from "outside".
*
* @return the internal id
*/
@Id
@GeneratedValue
@Column(name="id")
public long getId ()
{
return mId;
}
/**
* Sets the internal ID of this entry.
* Normally this isn't used from "outside".
*
* @param anId
*/
public void setId (long anId)
{
mId = anId;
}
/**
* Returns the name of this entry.<br>
* In case of type {@link DBFileMetaData#TYPE_FILE}, this will return the directory name without any '/' or parent directory names<br>
* In case of type {@link DBFileMetaData#TYPE_FILE}, this will return the filename, without any directory names
*
* @return the entry's name
*/
@Column(name="name")
public String getName ()
{
return mName.toLowerCase();
}
/**
* Sets the name of this entry.
*
* In case of type {@link DBFileMetaData#TYPE_DIR}, the directory's name without any '/' or parent directory names must be used<br>
* In case of type {@link DBFileMetaData#TYPE_FILE}, the filename, without any directory names must be used
*
* @param anName entry's name
*/
public void setName (String anName)
{
mName = anName.toLowerCase();
}
/**
* Returns the path in which the entry is located.
*
* @return the entry's parent path
*/
@Column(name="path")
public String getPath ()
{
return mPath.toLowerCase();
}
/**
* Sets the parent path in which the entry is located.
*
* @param anPath the parent path of this' entry
*
*/
public void setPath (String anPath)
{
mPath = anPath.toLowerCase();
}
/**
* Returns the entry's size. In case of type {@link DBFileMetaData#TYPE_DIR}, the size is always 0
*
* @return the entry's size measured in bytes
*/
@Column(name="size")
public int getSize ()
{
return mSize;
}
/**
* Sets the size of this entry. In case of {@link DBFileMetaData#TYPE_DIR} this must be 0.
*
* @param anSize
*/
public void setSize (int anSize)
{
mSize = anSize;
}
/**
* Returns the "last modified" date. As no "edit" operations are allowed on {@link IFileSystem}, this only reflects the "create date"
* @return last modified date
*/
@Column(name="last_modified")
public Date getLastModified ()
{
return lastModified;
}
/**
* Sets the timestamp at which the entry was created.
*
* @param anDate the date of creation
*/
public void setLastModified (Date anDate)
{
lastModified = anDate;
}
/**
* Returns the type of this db entry.
*
* @see DBFileMetaData#TYPE_DIR
* @see DBFileMetaData#TYPE_FILE
* @return the file entry's type
*/
@Column(name="type")
public int getType ()
{
return mType;
}
/**
* Sets the type of this entry.
*
* @see DBFileMetaData#TYPE_DIR
* @see DBFileMetaData#TYPE_FILE
* @param anType the type
*/
public void setType (int anType)
{
mType = anType;
}
/**
*
* Is the current entry a folder?
*
* @return true, if this object describes a folder, false if not
*/
@Transient
public boolean isDirectory(){
return (mType==TYPE_DIR ? true : false);
}
/**
* @see java.lang.Object#toString()
*/
@Override
@Transient
public String toString ()
{
StringBuffer sb = new StringBuffer();
sb.append("[id=");
sb.append(mId);
sb.append("|name=");
sb.append(mName);
sb.append("|path=");
sb.append(mPath);
sb.append("|size=");
sb.append(mSize);
sb.append("|type=");
sb.append(mType);
sb.append("]");
return sb.toString();
}
}
Und ich habe eine weitere Entity, die von der ersten erbt:
Java:
@Entity
@Table(name = "cms_medc_files")
public class DBFile extends DBFileMetaData
implements Serializable
{
private static final long serialVersionUID = 2L;
private byte[] mData;
/**
* Returns the file entry's data
*
* @return the complete data of this file in a byte[]
*/
@Column(name="data")
public byte[] getData ()
{
return mData;
}
/**
* Sets the data that belongs to the current file.
*
* @param anData the complete file data as an byte[]
*/
public void setData (byte[] anData)
{
mData = anData;
}
}
Und eine Abfragelogik die wie folgt ausschaut:
Java:
public List<DBFileMetaData> list (String aPath)
{
long start = System.currentTimeMillis();
aPath = aPath.toLowerCase();
mLogger.debug("begin. aPath=",aPath);
Query query = mEntityManager.createQuery("FROM DBFileMetaData WHERE path=:PATH ORDER BY NAME");
query.setParameter("PATH", aPath);
List<DBFileMetaData> resultList = query.getResultList();
if (mLogger.isDebugEnabled()) {
for (DBFileMetaData fileMetaData : resultList)
{
mLogger.debug("entry="+fileMetaData);
}
}
mLogger.debug("end. resultList.length=",resultList.size()," duration: ",(System.currentTimeMillis()-start)," ms");
return resultList;
}
Hintergrund der Sache:
Über die Datenbank ist ein kleines aber feines Dateisystem realisiert. Ich bin mir bewusst, dass das nicht der optimale Weg ist eine Datei auf dem Server abzulegen. Aber in diesem Fall gings nicht anders ohne einen extremen Aufwand zu bewältigen.
In der Datenbank (PostgreSQL 8.3) liegen rund 600 Einträge. Manche davon sind "Verzeichnisse" und haben somit keine Binärdaten, andere wiederum sind Dateien und haben Binärdaten mit bis zu 10MB Größe.
Die oben gezeigte Methode soll den Inhalt eines Verzeichnisses auflisten OHNE die Binärdaten mit abzufragen.
In einem Verzeichnis das 83 Dateien mit einer Gesamtgröße von etwa 100MB hat dauert die Abfrage auf unserem System um die 10sec und die CPU schnellt auf 80-100%
Mache ich die Abfrage auf ein Verzeichnis das weniger Datein hat (und somit auch weniger Gesamt-MB), dann geht die Abfrage rasend schnell und die CPU ist davon wenig beeindruckt.
Kann mir jemand sagen - mal abgesehen davon dass man Dateien nicht in einer DB speichern sollte - was hier falsch läuft? Hab ich die Entity falsch aufgebaut? Ist die Abfrage schlecht gewählt?
Meine Vermuting ist dass es mit der Vererbung der Entity Probleme gibt und eine Abfrage auf "nur die Meta-Daten" auch noch die Binärdaten irgendwie mit einbezieht ...
Gruß
Alex