Subato

DataObject

Studieren Sie den zur Aufgabe gehörenden Lehrbrief und lösen Sie die Aufgaben darin. Testen Sie ihre Lösungen zunächst am besten interaktiv in der JShell.


package name.panitz.util; import java.io.*; import java.util.*; import static java.util.Map.entry; import java.sql.*; import org.w3c.dom.*; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.*; import javax.xml.transform.stream.*; public interface DataObject { UID uid(); default void setUID(long id) { uid().uid = id; } static void checkPersistanceCapable(Class<?> klasse) { if (!klasse.isRecord()) throw new RuntimeException("class is not a record: " + klasse); if (!DataObject.class.isAssignableFrom(klasse)) throw new RuntimeException("class is not a DataObject: " + klasse); if (klasse.getTypeParameters().length > 0) throw new RuntimeException ("Persistence of generic DataObject class is not supported: " + klasse); } static String createStatement(Class<?> klasse) { checkPersistanceCapable(klasse); var r = new StringBuffer("CREATE TABLE "); r.append( klasse.getSimpleName() + " ("); var first = true; var rcs = klasse.getRecordComponents(); for (var rc : rcs) { if (first) first = !first; else r.append(", "); r.append(rc.getName() + " "); var typ = rc.getType(); var dbt = dbTypes.get(typ.getName()); if (null!=dbt) { r.append(dbt); } else if (typ == String.class) { var an = rc.getAnnotation(VARCHAR.class); r.append("VARCHAR("+(null == an? 100: an.value()) + ")"); } else if (DataObject.class.isAssignableFrom(typ)) { r.append("BIGINT"); // TODO foreign key (...) } else if (typ == UID.class) { r.append("BIGINT NOT NULL AUTO_INCREMENT"); } else { throw new RuntimeException("unsupported record field type: " + rc); } } r.append(", PRIMARY KEY (uid));"); return r.toString(); } static Map<String,String> dbTypes = Map.ofEntries (entry("byte", "TINYINT"),entry("Byte", "TINYINT") ,entry("short", "SMALLINT"),entry("Short", "SMALLINT") ,entry("int", "INT"),entry("Integer", "INT") ,entry("long", "BIGINT"),entry("Long", "BIGINT") ,entry("float", "REAL"),entry("Float", "REAL") ,entry("double", "DOUBLE"),entry("Double", "DOUBLE") ,entry("boolean", "BOOLEAN"),entry("Boolean", "BOOLEN") ); static void createTable(Connection con, Class<?> klasse) { try { con.createStatement().execute(createStatement(klasse)); } catch (SQLException e) { e.printStackTrace(); } } default void save(Connection con) { try { var klasse = this.getClass(); checkPersistanceCapable(klasse); var fieldNames = new StringBuffer(" ("); var fieldValues = new StringBuffer(); var rcs = klasse.getRecordComponents(); var first = true; boolean isUpdate = true; for (var rc : rcs) { var recordData = rc.getAccessor().invoke(this); if (rc.getName().equals("uid") && ((UID) recordData).uid == -1) { isUpdate = false; continue; } if (first) first = !first; else { fieldNames.append(", "); fieldValues.append(", "); } fieldNames.append(rc.getName()); if (isUpdate) fieldValues.append(rc.getName() + " = "); var typ = rc.getType(); //recordData.getClass(); if (dbTypes.get(typ.getName())!=null || typ == String.class) { fieldValues.append("\'" + recordData + "\'"); } else if (typ == UID.class) { fieldValues.append("\'" + ((UID) recordData).uid + "\'"); } else if (DataObject.class.isAssignableFrom(typ)) { if (null==recordData){ fieldValues.append("\'-1\'"); }else{ var c = ((DataObject) recordData); c.save(con); fieldValues.append("\'" + c.uid().uid + "\'"); } } else { throw new RuntimeException("unsupported record field type: " + rc); } } fieldNames.append(")"); var sql = new StringBuffer(isUpdate ? "UPDATE " : "INSERT INTO "); sql.append(klasse.getSimpleName()); if (!isUpdate){ sql.append(fieldNames); sql.append(" VALUES ("); }else sql.append(" SET "); sql.append(fieldValues); if (!isUpdate) sql.append(");"); else sql.append(" WHERE uid = " + uid().uid + ";"); var statement = con.prepareStatement (sql.toString(), Statement.RETURN_GENERATED_KEYS); statement.executeUpdate(); var rs = statement.getGeneratedKeys(); if (rs.next()) { setUID(rs.getLong(1)); } } catch (Exception e) { throw new RuntimeException(e); } } static record Selector(String column, String value) { } @SuppressWarnings("unchecked") static <C> List<C> select(Connection con,Class<C> klasse,Selector...sls){ checkPersistanceCapable(klasse); try { var sql = new StringBuffer("SELECT * FROM " + klasse.getSimpleName()); if (sls.length > 0) sql.append(" WHERE "); var first = true; for (var sel : sls) { if (first)first = false; else sql.append(", "); sql.append(sel.column); sql.append(" = '"); sql.append(sel.value()); sql.append("'"); } sql.append(";"); var stat = con.createStatement(); var rs = stat.executeQuery(sql.toString()); var result = new ArrayList<C>(); while (rs.next()) { var rcs = klasse.getRecordComponents(); var args = new Object[rcs.length]; int i = 0; for (var rc : rcs) { var typ1 = rc.getType(); if (typ1 == boolean.class || typ1 == Boolean.class) args[i] = rs.getBoolean(rc.getName()); else if (typ1 == int.class || typ1 == Integer.class) args[i] = rs.getInt(rc.getName()); else if (typ1 == long.class || typ1 == Long.class) args[i] = rs.getLong(rc.getName()); else if (typ1 == float.class || typ1 == Float.class) args[i] = rs.getFloat(rc.getName()); else if (typ1 == double.class || typ1 == Double.class) args[i] = rs.getDouble(rc.getName()); else if (typ1 == byte.class || typ1 == Byte.class) args[i] = rs.getByte(rc.getName()); else if (typ1 == short.class || typ1 == Short.class) args[i] = rs.getShort(rc.getName()); else if (typ1 == String.class) args[i] = rs.getString(rc.getName()); else if (DataObject.class.isAssignableFrom(typ1)){ var fid = rs.getLong(rc.getName()); if (fid==-1){ args[i] = null; }else{ args[i] = select(con, typ1, new Selector("uid", fid + "")).get(0); } } else if (typ1 == UID.class) args[i] = new UID(rs.getLong(rc.getName())); else throw new RuntimeException("unsupported type: " + typ1); i++; } var constr = Arrays.stream(klasse.getConstructors()) .reduce((r, x)->r.getParameterCount() > x.getParameterCount() ? r : x); C elem = (C) constr.get().newInstance(args); result.add(elem); } return result; } catch (Exception e) { throw new RuntimeException(e); } } default void delete(Connection con){ //TODO } default void deepDelete(Connection con){ //TODO } default Element toXML() { try { var doc = DocumentBuilderFactory.newInstance() .newDocumentBuilder().newDocument(); return toXML(doc); } catch (Exception e) { throw new RuntimeException(e); } } default Element toXML(Document doc) { try { var r=doc.createElement(getClass().getName().replaceAll("\\$","")); r.setAttribute("type", this.getClass().getName()); var klasse = this.getClass(); var rcs = klasse.getRecordComponents(); for (var rc : rcs) { /* TODO Hier sind jetzt rekursiv die Objekte des Record-Felder als Kinder des DataObjektes einzutragen. Die Kindelemente haben als Tagnamen den Namen des Record-Feldes. Jedes Kindelement soll auch das Attribute type haben, das den Wert des Laufzeittypen des Record-Feldes hat. */ } return r; } catch (Exception e) { throw new RuntimeException(e); } } List<Class<?>> primWrapper = List.of(Integer.class, Long.class, Boolean.class, Short.class ,Character.class, Byte.class, Float.class, Double.class); default String toXMLString() { var writer = new StringWriter(); try { var transformer = TransformerFactory.newInstance().newTransformer(); transformer.transform (new DOMSource(toXML()), new StreamResult(writer)); return writer.toString(); } catch (Exception e) { throw new RuntimeException(e); } } @SuppressWarnings("unchecked") static <C> C fromXML(Class<C> klasse, Element node) { try { if (!DataObject.class.isAssignableFrom(klasse)) throw new RuntimeException("class is not a DataObject: " + klasse); var rcs = klasse.getRecordComponents(); var cs = node.getChildNodes(); var args = new Object[rcs.length - 1]; //ohne uid, deshalb -1 var i = 0; for (var rc : rcs) { if (rc.getName().equals("uid")) continue; Node child = cs.item(i); if (child.getNodeType() != Node.ELEMENT_NODE) throw new RuntimeException("Not an Element"); /* TODO Lesen Sie die Kindknoten des Elements ein. Erzeugen Sie für die die entsprechenden Objekte, die sie in die Argumente einfügen. Das Vorgehen entspricht in Teilen den hinteren Teil der select Methode. */ i++; } //der Konstruktor ohne uid, also der mit weniger Argumenten var constr = Arrays.stream(klasse.getConstructors()) .reduce((r, x)->r.getParameterCount()==args.length?r:x); return (C) constr.get().newInstance(args); } catch (Exception e) { throw new RuntimeException(e); } } static <C> C fromXML(Class<C> klasse, InputStream in) { try { return fromXML (klasse , DocumentBuilderFactory .newInstance() .newDocumentBuilder() .parse(in).getDocumentElement()); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(); } } static <C> C fromXML(Class<C> klasse, String in) { return fromXML(klasse, new ByteArrayInputStream(in.getBytes())); } String JDBC_DRIVER = "org.h2.Driver"; String DB_URL = "jdbc:h2:./userData/test1"; // Database credentials String USER = "sa"; String PASS = ""; static Connection getConnection() { try { Class.forName(JDBC_DRIVER); return DriverManager.getConnection(DB_URL, USER, PASS); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } }; }
java
You are not logged in and therefore you cannot submit a solution.