001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021 022package org.biojavax.bio.db.biosql; 023import java.lang.reflect.Constructor; 024import java.lang.reflect.Method; 025import java.util.ArrayList; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import org.biojavax.DocRefAuthor; 032import org.biojavax.RichObjectBuilder; 033import org.biojavax.SimpleCrossRef; 034import org.biojavax.SimpleDocRef; 035import org.biojavax.SimpleNamespace; 036import org.biojavax.bio.taxa.SimpleNCBITaxon; 037import org.biojavax.ontology.SimpleComparableOntology; 038 039 040/** 041 * Takes requests for RichObjects and sees if it can load them from a Hibernate 042 * database. If it can, it returns the loaded objects. Else, it creates them 043 * and persists them, then returns them. Doesn't retain a memory map so runs 044 * a lot of Hibernate queries if used frequently, but on the plus side this 045 * makes it memory-efficient. 046 * @author Richard Holland 047 * @author David Scott 048 * @author Deepak Sheoran 049 * @since 1.5 050 */ 051public class BioSQLRichObjectBuilder implements RichObjectBuilder { 052 053 private Object session; 054 private Method createQuery; 055 private Method setParameter; 056 private Method uniqueResult; 057 private Method persist; 058 059 /** 060 * Creates a new instance of SimpleRichObjectBuilder. The session parameter 061 * is a Hibernate Session object and must not be null. It is this session 062 * that database objects will be retrieved from/persisted to. 063 * @see <a href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/Session.html"> org.hibernate.Session</a> 064 */ 065 public BioSQLRichObjectBuilder(Object session) { 066 try { 067 // Lazy load the Session class from Hibernate. 068 Class hibernateSession = session.getClass(); 069 Class realHibernateSession = Class.forName("org.hibernate.Session"); 070 // Test to see if our parameter is really a Session 071 if (!realHibernateSession.isAssignableFrom(hibernateSession)) 072 throw new IllegalArgumentException("Parameter must be a org.hibernate.Session object"); 073 this.session = session; 074 // Lookup the createQuery and persist methods 075 this.createQuery = hibernateSession.getMethod("createQuery", new Class[]{String.class}); 076 this.persist = hibernateSession.getMethod("persist", new Class[]{String.class,Object.class}); 077 // Lazy load the Query class from Hibernate. 078 Class hibernateQuery = Class.forName("org.hibernate.Query"); 079 // Lookup the setParameter and uniqueQuery methods 080 this.setParameter = hibernateQuery.getMethod("setParameter", new Class[]{int.class,Object.class}); 081 this.uniqueResult = hibernateQuery.getMethod("uniqueResult", new Class[]{}); 082 } catch (ClassNotFoundException e) { 083 throw new RuntimeException(e); 084 } catch (NoSuchMethodException e) { 085 throw new RuntimeException(e); 086 } 087 } 088 089 /** 090 * {@inheritDoc} 091 * Attempts to look up the details of the object in the database. If it 092 * finds them it loads the object and returns it. Else, it persists the 093 * wrapper object it made to do the search with and returns that. 094 */ 095 public Object buildObject(Class clazz, List paramsList) { 096 // convert the params list to remove nulls as we can't process those. 097 List ourParamsList = new ArrayList(paramsList); 098 for (Iterator i = ourParamsList.iterator(); i.hasNext(); ) 099 if (i.next()==null) i.remove(); 100 // Run the query. 101 try { 102 // Create the Hibernate query to look it up with 103 String queryText; 104 String queryType; 105 if (SimpleNamespace.class.isAssignableFrom(clazz)) { 106 queryText = "from Namespace as ns where ns.name = ?"; 107 queryType = "Namespace"; 108 } else if (SimpleComparableOntology.class.isAssignableFrom(clazz)) { 109 queryText = "from Ontology as o where o.name = ?"; 110 queryType = "Ontology"; 111 } else if (SimpleNCBITaxon.class.isAssignableFrom(clazz)) { 112 queryText = "from Taxon as o where o.NCBITaxID = ?"; 113 queryType = "Taxon"; 114 } else if (SimpleCrossRef.class.isAssignableFrom(clazz)) { 115 queryText = "from CrossRef as cr where cr.dbname = ? and cr.accession = ? and cr.version = ?"; 116 queryType = "CrossRef"; 117 } else if (SimpleDocRef.class.isAssignableFrom(clazz)) { 118 // First check if record exists with pubmed or medline id, if provided 119 if (ourParamsList.size() > 3) { 120 List crossRefParams = new ArrayList(); 121 String crossRefQueryText = "from DocRef as dr where dr.crossref = (select id from CrossRef as cref where cref.dbname = ? and cref.accession = ? and cref.version = ?)"; 122 crossRefParams.add(ourParamsList.get(ourParamsList.size() - 3)); // get dbname 123 crossRefParams.add(ourParamsList.get(ourParamsList.size() - 2)); // get accession 124 crossRefParams.add(ourParamsList.get(ourParamsList.size() - 1)); // get version 125 // Build the query object 126 Object query = this.createQuery.invoke(this.session, new Object[]{crossRefQueryText}); 127 // Set the parameters 128 for (int i = 0; i < crossRefParams.size(); i++) { 129 query = this.setParameter.invoke(query, new Object[]{new Integer(i), crossRefParams.get(i)}); 130 } 131 // Get the results 132 Object result = this.uniqueResult.invoke(query, (Object[])null); 133 // Return the found object, if found 134 if (result!=null) return result; 135 // If we get here, resort to looking up by author and title and remove all references 136 // to pubmed/medline from params list. 137 ourParamsList.remove(ourParamsList.size() - 3); 138 ourParamsList.remove(ourParamsList.size() - 2); 139 ourParamsList.remove(ourParamsList.size() - 1); 140 } 141 queryType = "DocRef"; 142 // convert List constructor to String representation for query 143 ourParamsList.set(0, DocRefAuthor.Tools.generateAuthorString((List)ourParamsList.get(0), true)); 144 if (ourParamsList.size()<3) { 145 queryText = "from DocRef as cr where cr.authors = ? and cr.location = ? and cr.title is null"; 146 } else { 147 queryText = "from DocRef as cr where cr.authors = ? and cr.location = ? and cr.title = ?"; 148 } 149 } else throw new IllegalArgumentException("Don't know how to handle objects of type "+clazz); 150 // Build the query object 151 Object query = this.createQuery.invoke(this.session, new Object[]{queryText}); 152 // Set the parameters 153 for (int i = 0; i < ourParamsList.size(); i++) { 154 query = this.setParameter.invoke(query, new Object[]{new Integer(i), ourParamsList.get(i)}); 155 } 156 // Get the results 157 Object result = this.uniqueResult.invoke(query, (Object[])null); 158 // Return the found object, if found 159 if (result!=null) return result; 160 // Create, persist and return the new object otherwise 161 else { 162 // Load the class 163 Class[] types = new Class[ourParamsList.size()]; 164 // Find its constructor with given params 165 for (int i = 0; i < ourParamsList.size(); i++) { 166 if (ourParamsList.get(i) instanceof Set) types[i] = Set.class; 167 else if (ourParamsList.get(i) instanceof Map) types[i] = Map.class; 168 else if (ourParamsList.get(i) instanceof List) types[i] = List.class; 169 else types[i] = ourParamsList.get(i).getClass(); 170 } 171 Constructor c = clazz.getConstructor(types); 172 // Instantiate it with the parameters 173 Object o = c.newInstance(ourParamsList.toArray()); 174 // Persist and return it. 175 this.persist.invoke(this.session, new Object[]{queryType,o}); 176 return o; 177 } 178 } catch (IllegalArgumentException ex) { 179 throw ex; 180 } catch (Exception e) { 181 // Write a useful message explaining what we were trying to do. It will 182 // be in the form "class(param,param...)". 183 StringBuffer paramsstuff = new StringBuffer(); 184 paramsstuff.append(clazz); 185 paramsstuff.append("("); 186 for (int i = 0; i < ourParamsList.size(); i++) { 187 if (i>0) paramsstuff.append(","); 188 paramsstuff.append(ourParamsList.get(i).getClass()); 189 } 190 paramsstuff.append(")"); 191 // Throw the exception with our nice message 192 throw new RuntimeException("Error while trying to call new "+paramsstuff,e); 193 } 194 } 195 196}