Avatar billede kkaen Nybegynder
28. april 2010 - 14:59 Der er 23 kommentarer og
1 løsning

Form-indtastet streng skal indsættes i database

Jeg har en form, hvori jeg indtaster data, som kan indsættes i en mysql-database. Der er også mulighed for at hente tidligere poster, modificere dem, og updatere en post. Dette har tidligere virket.

Nu er jeg ved at lave det sådan, at hvis brugeren indtaster et linieskift manuelt, så kommer dette også med i databasen. Jeg har læst på nettet, at et linieskift sagtens kan håndteres i databasen, eftersom det blot automatisk angives som "\n".
Jeg troede egentlig, at dette var det nuværende problem.

Men nu har jeg lige lavet en "normal" opdatering af posten - altså uden linieskift i. Men nu giver det stadig problemer. Findes der en måde, hvorpå man kan komme nærmere den nøjagtige fejl?

Jeg udvikler i Netbeans, og under kørsel har jeg udskrevet sql-en. Denne har jeg manuelt sat ind i et sql-windue i netbeans, og eksekveret. Den tillader execution'en, så syntax'en er vel rigtigt nok? Når jeg så laver en "select all" på tabellen, så er de forventede ændringer ikke at finde????
Avatar billede arne_v Ekspert
28. april 2010 - 15:05 #1
Jeg vil klart anbefale PreparedStatement (eller ORM som f.eks. Hibernate) for data med linieskift i.

Men saa boer det ogsaa virke.

Udskriv UPDATE.

Sikker dig at du enten har auto commit true eller laver eksplicit commit.

Du kan ogsaa checke hvor mange raekker opdateringen mener at have opdateret.
Avatar billede kkaen Nybegynder
28. april 2010 - 15:10 #2
Det vil sige, at jeg skal ændre mit manuelt konstruerede "UPDATE"-statement i min java-klasse, så der benyttes "PreparedStatement"?

Jeg skal til at læse lidt om Hibernate, men det kommer ikke til at være lige foreløbig. Så hvis en løsning på den gode "gammeldags" måde kan strikkes sammen, så vil det være bedst.

Fra "UPDATE" i dit indlæg, omhandler det så Hibernate? For min sql-sætning er jo med "Update" i begyndelsen, og den har jo også virket førhen. Kan man angive en "auto commit" et sted i java, eller var det Hibernate?
Avatar billede arne_v Ekspert
28. april 2010 - 15:21 #3
Jeg ved ikke om Statement virker med linieskift, men PreparedStatement vil virke. Og som side effekt beskytter det jo ogsaa mod SQL injection.

Auto commit er en egenskab paa Connection (og dermed JDBC). Jeg siger ikke at det er det, men det er en klassisk fejl: man koerer en masse UPDATE og INSERT med de gemmes ikke - fordi at man ikke fik aendringerne committet.
Avatar billede kkaen Nybegynder
28. april 2010 - 15:22 #4
Ah, jeg tror, jeg har fundet fejlen nu. Primær-nøglen er et tal med 5 decimaler. Og mit update-statement har kun 4 decimaler i sig. Så det lyder som en forklaring. Jeg tester lige i morgen.
Avatar billede arne_v Ekspert
28. april 2010 - 15:28 #5
Decimaler??

Der er doedstraf for at bruge REAL/FLOAT/DOUBLE som primary key !
Avatar billede fsconsult.dk Nybegynder
29. april 2010 - 07:51 #6
mon ikke han mener cifre, og ikke decimaler :)
Avatar billede kkaen Nybegynder
29. april 2010 - 08:28 #7
#5:

Jeg havde det godt på fornemmelsen i begyndelsen, men efter grundig overvejelse kom jeg frem til: "hvorfor er det dét?"

Så vidt jeg ved af, må man gerne bruge int's. Og en double er vel det samme, bare med et komma i. Så en evt. problemstilling burde man da kunne programmere sig ud af. Det eneste lille problem, jeg kan se, er, at der i teorien kan være et uendeligt antal decimaler på en primærnøgle, som derfor ikke kan lokaliseres i databasen. Men dette kan man da lave en programmerings-mæssig løsning på...?
Avatar billede fsconsult.dk Nybegynder
29. april 2010 - 09:10 #8
Floating point er pr. definition unøjagtige (nogle værdier "findes" ikke). Ønsker du præcission OG decimaler, så brug DECIMAL (men forstår ikke hvorfor en entydig ikke-brugervendt nøgle, skulle have decimaler).
Avatar billede kkaen Nybegynder
29. april 2010 - 09:21 #9
"(men forstår ikke hvorfor en entydig ikke-brugervendt nøgle, skulle have decimaler)."
->det skal den i dette tilfælde fordi, den repræsenterer koordinater i landskabet. Og de angives med decimaler. Men når man kommer ud på 5 decimaler, så er nøjagtigheden på under 1 meter, hvilket må siges at være godt nok i dette tilfælde.

Mine overvejelser i beslutningsprocessen mht. at bruge en anden nøgle som primary key var, at når der alligevel skal checkes i systemet, om en aktuel koordinat findes i forvejen, og der ud fra tillade eller acceptere indtastningen, så er det vel blot et check for checkets skyld. Altså unødvendige beregninger som sløver systemet ned. Så kan man vel lige så godt klare det hele i første hug, og lave primary key ud fra koordinatet?
Avatar billede kkaen Nybegynder
29. april 2010 - 10:26 #10
#4
Antagelsen var korrekt. Fejlen er rettet nu, og systemet kører som forventet.
Avatar billede kkaen Nybegynder
29. april 2010 - 16:14 #11
Hvad er vurderingen af mine overvejelser i andet afsnit i #9 ?
Avatar billede arne_v Ekspert
29. april 2010 - 18:17 #12
Java int & database INTEGER er fint som PK
Java BigDecimal & database DECIMAL (eller NUMBER) er fint som PK
Java double (eller float) & database ditto duer ikke som PK

PK's formaal er at finde en enkelt raekke med WHERE felt=vaerdi.

Floating point har nogle egenskaber som goer at = til sammenligning er ret meningsloest.

Man bruger ikke if(x==y) men if(Math.abs(x-y)<EPS). Det fungerer ikke i database sammenhaeng.
Avatar billede kkaen Nybegynder
03. maj 2010 - 08:14 #13
#12

Jeg bruger ikke koordinaterne, som er double, til beregninger eller andet fancy. Jeg bruger det udelukkende til toString(), så de kan udskrives og sammenlignes med andre koordinater med samme antal decimaler.

Med det i mente, overholder mit system vel stadigvæk det angivne i #12 ?
Avatar billede arne_v Ekspert
03. maj 2010 - 15:03 #14
"Floating point har nogle egenskaber som goer at = til sammenligning er ret meningsloest."

"og sammenlignes med andre koordinater med samme antal decimaler."

lyder de kompatible?
Avatar billede kkaen Nybegynder
03. maj 2010 - 18:22 #15
Tja, det skulle jeg da mene. Hvis man typecaster en streng til double, så kan den vel godt sammenlignes med en anden typecastet streng?

Ellers mangler jeg stadig et godt argument på de negative "egenskaber".
Avatar billede arne_v Ekspert
09. maj 2010 - 04:21 #16
Hvis de har samme antal decimaler, så kan du sammenligne dem som strenge.

Hvis du sammenligner dem som floating point, så kan du godt opleve sære ting.

public class FloatSucks {
    public static void main(String[] args) {
        for(int i = 1; i <= 12; i++) {
            String s = "100000000000".substring(0, i);
            String s1 = s + ".0";
            String s2 = s + ".1";
            System.out.println(s1 + " = " + s2 + " : " + s1.equals(s2) + " " + (Float.parseFloat(s1) == Float.parseFloat(s2)));
        }
    }
}

udskriver:

1.0 = 1.1 : false false
10.0 = 10.1 : false false
100.0 = 100.1 : false false
1000.0 = 1000.1 : false false
10000.0 = 10000.1 : false false
100000.0 = 100000.1 : false false
1000000.0 = 1000000.1 : false false
10000000.0 = 10000000.1 : false true
100000000.0 = 100000000.1 : false true
1000000000.0 = 1000000000.1 : false true
10000000000.0 = 10000000000.1 : false true
100000000000.0 = 100000000000.1 : false true
Avatar billede kkaen Nybegynder
10. maj 2010 - 16:42 #17
#16
Ja, det mente jeg nu også godt jeg kunne, når det var samme antal decimaler og strings.

Og jeg kan godt se problematikken i dit eksempel. Faktisk har jeg netop oplevet et lignende problem i dag. Jeg skal ikke kunne sige, om det var nøjagtig samme problemstilling, men eftersom det var floats, der blev behandlet, og de kom ud af "maskineriet" som fejlfortolket (funktion af Google Maps-framework), så konkluderede jeg, at det nok var float'en som sådan, den var gal med.
Avatar billede arne_v Ekspert
10. maj 2010 - 16:56 #18
10.0f skal laeses som 10.0 +/- 0.000001 og 10000.0f skal laeses som 10000.0 +/- 0.001 og 100000000.0f skal laeses som 100000000.0 +/- 10.0 !

Det er ganske glimrende til alt der skal maales.

Hvis man har 50 km til arbejde, saa har man ikke et problem med at det betyder 50 +/- 0.0000005 da det jo betyder 50 km +/- 5 mm og det giver ingen mening at diskutere millimeter i den sammenhaeng.

Men det er ikke godt til penge *i de fleste sammenhaenge).

Og det er heller ikke godt til == hverken i app eller i database.

Jeg lavede det her MySQL eksempel til et andet spoergsmaal for nogle aar siden:

mysql> CREATE TABLE fproblem (id INT PRIMARY KEY,x FLOAT(12,2));
Query OK, 0 rows affected (0.09 sec)

mysql>
mysql> INSERT INTO fproblem VALUES (1, 1000000000);
Query OK, 1 row affected (0.00 sec)

mysql>
mysql> SELECT x FROM fproblem WHERE id=1;
+---------------+
| x            |
+---------------+
| 1000000000.00 |
+---------------+
1 row in set (0.00 sec)

mysql>
mysql> UPDATE fproblem SET x=x+1000 WHERE id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql>
mysql> SELECT x FROM fproblem WHERE id=1;
+---------------+
| x            |
+---------------+
| 1000001024.00 |
+---------------+
1 row in set (0.00 sec)

mysql>
mysql> DROP TABLE fproblem;
Query OK, 0 rows affected (0.00 sec)

Proev og forklar den til en bogholder !!
Avatar billede kkaen Nybegynder
13. maj 2010 - 12:17 #19
Ja, det kan jeg godt se. Lækkert med udførligt eksempel :-)

Men jeg må dog lige påpege, at det tilsyneladende går godt i eksemplet, så længe der IKKE modificeres/overskrives med beregninger.
Hvis der bruges float til primary key, så er det vel ikke for at modificere nøglen eller overskrive den ud fra beregninger. En primary key skal der ikke pilles ved - hvis den skal ændres, skal row'en slettes, og en ny row med passende primary key oprettes. Det er vel den gænge standart? Og i så fald duer eksemplet ikke...
Avatar billede arne_v Ekspert
13. maj 2010 - 20:19 #20
Jeg vil sige at problemerne er stoerst hvis man regner paa tallene.

Men se foelgende lille program:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

public class FProblem {
    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        Connection con = DriverManager.getConnection("jdbc:mysql://localhost/Test", "root", "");
        Statement stmt = con.createStatement();
        stmt.executeUpdate("CREATE TABLE fproblem (x FLOAT(12,2) PRIMARY KEY, s VARCHAR(50))");
        PreparedStatement pstmt = con.prepareStatement("INSERT INTO fproblem VALUES(?,?)");
        pstmt.setFloat(1, 1000000000.0f);
        pstmt.setString(2, "1000000000.0f");
        pstmt.executeUpdate();
        pstmt = con.prepareStatement("SELECT * FROM fproblem WHERE x = ?");
        pstmt.setFloat(1, 1000000007.0f); // <----- laeg maerke til 7 tallet
        ResultSet rs = pstmt.executeQuery();
        while(rs.next()) {
            System.out.println(rs.getFloat(1) + " " + rs.getString(2));
        }
        rs.close();
        stmt.executeUpdate("DROP TABLE fproblem");
        con.close();
    }
}

udskriver:

1.0E9 1000000000.0f
Avatar billede kkaen Nybegynder
14. maj 2010 - 11:05 #21
Ja, så er vi enige :-)

Jeg fik dog lige den tanke i dit sidste eksempel, at hvad nu hvis ".0f" ligger udenfor sammenlignings-intervallet? Så vil det jo forklare, hvorfor der ikke bliver udskrevet det forventede eftersom det vel er en afrunding.

Jeg kan dog ikke helt finde sammenhængen med syntaxen, og så forklaringen på syntaxen i #18.
Avatar billede arne_v Ekspert
14. maj 2010 - 20:17 #22
1000000000.0f er 1000000000 +/- 100
1000000007.0f er 1000000007 +/- 100

er oplagt ens.
Avatar billede arne_v Ekspert
14. maj 2010 - 20:19 #23
Naturligvis kan man med meget omhyggelig programmering slippe udenom problemerne med floating point.

Men man skal have en ret god grund til at ville bruge saa meget tid (laes: penge) paa det fremfor at bruge BigDecimal/DECIMAL.
Avatar billede arne_v Ekspert
13. juni 2010 - 04:27 #24
Jeg vil tillade mig at smide et svar.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester