Java >> Java tutorial >  >> Java

jOOQ tirsdage:Ming-Yee Iu giver indsigt i sprogintegreret forespørgsel

Velkommen til jOOQ Tuesdays-serien. I denne serie udgiver vi en artikel den tredje tirsdag hver anden måned, hvor vi interviewer en, som vi finder spændende i vores branche fra et jOOQ-perspektiv. Dette inkluderer folk, der arbejder med SQL, Java, Open Source og en række andre relaterede emner.

Vi har fornøjelsen af ​​at tale med Ming-Yee Iu i denne ottende udgave, som vil fortælle os om, hvordan forskellige mennesker i vores branche har tacklet integrationen af ​​forespørgselssystemer i generelle sprog, herunder hans eget bibliotek JINQ, som gør det til Java .

Ming, alle, der kommer fra C# til Java, vil google LINQ til Java. Du har implementeret netop det med JINQ. Hvad fik dig til at gøre det?

Jinq voksede faktisk ud af min ph.d.-forskning på EPFL-universitetet i Schweiz. Da jeg startede en ph.d. der i 2005, havde jeg brug for et specialeemne, og jeg hørte, at min vejleder Willy Zwaenepoel var interesseret i at gøre det nemmere at skrive databasekode. Jeg havde lidt en baggrund med Java internals, fra da jeg var i praktik hos et af IBMs JVM-teams i 1997, så da jeg kiggede på problemet, så jeg på det fra et systemperspektiv på lavere niveau. Som et resultat kom jeg på ideen om at bruge et bytekode-omskrivningsskema til at omskrive visse typer Java-kode til databaseforespørgsler. Der var andre forskergrupper, der kiggede på problemet på samme tid, herunder LINQ-gruppen. Forskellige grupper kom med forskellige tilgange baseret på deres egen baggrund. Den grundlæggende antagelse var, at programmører havde svært ved at skrive databasekode, fordi der var et semantisk hul – den relationelle databasemodel var så forskellig fra den objektorienterede programmeringsmodel, at programmører spildte mental indsats på at bygge bro over forskellene. Håbet var, at denne semantiske kløft kunne reduceres ved at lade programmører skrive normal Java-kode og få computeren til at finde ud af, hvordan man kører denne kode på en database. Forskellige tilgange ville resultere i værktøjer, der kunne håndtere mere komplekse databaseforespørgsler eller kunne være mere fleksible i den kodestil, de accepterer.

Selvom jeg kom med en indledende tilgang ret hurtigt, tog det mig mange år at forfine algoritmerne til noget mere robust og brugbart. I lighed med LINQ-forskerne fandt jeg ud af, at mine algoritmer fungerede bedst med funktionel kode. Fordi kode i funktionel stil ikke har nogen bivirkninger, er det nemmere at analysere. Det er også nemmere at forklare programmører, hvordan man skriver kompleks kode, som algoritmerne stadig kan forstå. Desværre, da jeg afsluttede min ph.d. i 2010, understøttede Java stadig ikke funktionel programmering ordentligt, så jeg skrinlagde forskningen for at arbejde med andre ting. Men da Java 8 endelig udkom i 2014 med lambdas, besluttede jeg mig for at gense min gamle forskning. Jeg tilpassede min forskning til at gøre brug af Java 8 lambdas og til at integrere med nuværende virksomhedsværktøjer. Og resultatet blev Jinq, et open source-værktøj, der understøttede LINQ-lignende forespørgsler i Java.

I en nylig diskussion om reddit har du nævnt, at Java-sprogstewarderne aldrig vil integrere forespørgselssystemer i sproget, og at LINQ har været en fejl. Alligevel er LINQ uhyre populær i C#. Hvorfor var LINQ en fejl?

Min mening er lidt mere nuanceret end som så. LINQ giver meget mening for C#-økosystemet, men jeg synes, det er totalt upassende for Java. Forskellige sprog har forskellige afvejninger, forskellige filosofier og forskellig historisk bagage. At integrere et forespørgselssystem i Java ville være i modstrid med Java-filosofien og ville blive betragtet som en fejl. C# blev designet med forskellige afvejninger i tankerne, så det er mere acceptabelt at tilføje funktioner som forespørgselsintegration til C#.

C# er designet til at udvikle sig hurtigt. C# tvinger jævnligt programmører til at efterlade gammel kode, så den kan omfavne nye måder at gøre tingene på. Der er en gammel artikel om Joel om Software, der beskriver, hvordan Microsoft har to lejre:Raymond Chen-lejren, der altid forsøger at opretholde bagudkompatibilitet, og MSDN Magazine-lejren, der altid evangeliserer skinnende ny teknologi, som kan forlades efter et par år. Raymond Chen-lejren giver mig mulighed for at køre 20 år gamle Windows-programmer på Windows 10. MSDN Magazine-lejren producerer cool ny teknologi som C# og Typescript og LINQ. Der er intet galt med MSDN-filosofien. Mange programmører foretrækker at bruge sprog, der er bygget ved hjælp af denne filosofi, fordi API'erne og sprogene ender med mindre arv i dem. Du behøver ikke at forstå en API's 30-årige historie for at finde ud af den rigtige måde at bruge den på. Apple bruger denne filosofi, og mange programmører elsker det på trods af, at de skal omskrive al deres kode hvert par år for at tilpasse sig de nyeste API'er. For C# er det fint at anvende en teknologi, der er umoden og stadig under udvikling, fordi de kan opgive den senere, hvis det ikke lykkes.

Java-filosofien er aldrig at bryde baglæns kompatibilitet. Gammel Java-kode fra 1990'erne kompilerer stadig og kører perfekt på moderne Java. Som sådan er der en enorm vedligeholdelsesbyrde at tilføje nye funktioner til Java. Enhver funktion skal vedligeholdes i årtier. Når en funktion er føjet til Java, kan den ikke ændres, ellers kan den bryde bagudkompatibiliteten. Som følge heraf er det kun funktioner, der har bestået tidens tand, der er kandidater til at blive tilføjet til Java. Når funktioner føjes til Java, som endnu ikke er helt modne, "låser" det en specifik implementering og forhindrer funktionen i at udvikle sig, efterhånden som folks behov ændrer sig. Dette kan give store hovedpine for sproget i fremtiden.

Et eksempel på denne lock-in er Java-serialisering. Det er meget praktisk at være i stand til nemt at skrive objekter til disk. Men funktionen låste i en arkitektur, der ikke er fleksibel nok til fremtidige brugssager. Folk ønsker at serialisere objekter til JSON eller XML, men kan ikke gøre det ved at bruge den eksisterende serialiseringsramme. Serialisering har ført til mange sikkerhedsfejl, og der krævedes en enorm mængde udviklerressourcer for at få lambda'er og serialisering til at fungere korrekt sammen. Et andet eksempel på denne for tidlige låsning er synkroniseringsunderstøttelse for alle objekter. På det tidspunkt virkede det meget fremadskuende at have multi-threading-primitiver indbygget lige ind i sproget. Da hvert objekt kan bruges som en multi-threaded skærm, kan du nemt synkronisere adgangen til hvert objekt. Men vi ved nu, at gode flertrådede programmer undgår den slags finkornet synkronisering. Det er bedre at arbejde med synkroniseringsprimitiver på højere niveau. Al den synkronisering på lavt niveau sænker ydeevnen af ​​både enkelt- og flertrådskode. Selvom du ikke bruger funktionen, skal alle Java-objekter belastes af det overhead, der er forbundet med at have låseunderstøttelse. Serialisering og synkronisering blev begge tilføjet til Java med de bedste hensigter. Men disse funktioner behandles nu som "goto":de består ikke lugttesten. Hvis du ser en kode, der bruger disse funktioner, betyder det normalt, at koden skal undersøges ekstra.

Tilføjelse af LINQ-stil forespørgsler til Java vil sandsynligvis forårsage lignende problemer. Misforstå mig ikke. LINQ er et fantastisk system. Det er i øjeblikket det mest elegante system, vi har nu til at integrere et forespørgselssprog i et objektorienteret sprog. Mange mennesker elsker at bruge C# specifikt på grund af LINQ. Men den underliggende teknologi er stadig for umoden til at blive tilføjet til Java. Forskere kommer stadig med nyere og bedre måder at indlejre forespørgselssystemer i sprog, så der er en meget reel fare for at låse Java ind i en tilgang, der senere ville blive betragtet som forældet. Allerede nu har forskere mange forbedringer til LINQ, som Microsoft ikke kan anvende uden at opgive sin gamle kode.

For at oversætte LINQ-udtryk til databaseforespørgsler f.eks. tilføjede Microsoft noget funktionalitet til C#, der lader LINQ inspicere de abstrakte syntakstræer for lambda-udtryk under kørsel. Denne funktionalitet er praktisk, men den begrænser LINQ til kun at arbejde med udtryk. LINQ fungerer ikke med udsagn, fordi den ikke kan inspicere de abstrakte syntakstræer for lambdaer, der indeholder udsagn. Denne begrænsning på, hvilke typer lambdaer, der kan inspiceres, er elelegant. Selvom denne funktionalitet til at inspicere lambdaer er virkelig kraftfuld, er den så begrænset, at meget få andre frameworks bruger den. I et alment programmeringssprog bør alle sprogprimitiver være udtryksfulde nok til, at de kan bruges som byggeklodser til mange forskellige strukturer og rammer. Men denne lambda-inspektionsfunktionalitet er endt med kun at være nyttig til forespørgselsrammer som LINQ. Faktisk har Jinq vist, at denne funktionalitet ikke engang er nødvendig. Det er muligt at bygge et LINQ-lignende forespørgselssystem ved kun at bruge den kompilerede bytekode, og det resulterende forespørgselssystem ender med at være mere fleksibelt, idet det kan håndtere sætninger og andre imperative kodestrukturer.

Efterhånden som programmører har fået mere erfaring med LINQ, er de også begyndt at spekulere på, om der kunne være alternative tilgange, der ville fungere bedre end LINQ. LINQ formodes at gøre det lettere for programmører at skrive databaseforespørgsler, fordi de kan skrive funktionel kode i stedet for at skulle lære SQL. Men i virkeligheden, for at bruge LINQ godt, skal en programmør stadig også forstå SQL. Men hvis en programmør allerede forstår SQL, hvilke fordele giver LINQ dem? Ville det være bedre at bruge et forespørgselssystem som jOOQ, der matcher SQL-syntaksen tættere end Slick, og så hurtigt kan udvikle sig til at omfatte nye SQL-funktioner? Måske er forespørgselssystemer ikke engang nødvendige. Flere og flere virksomheder anvender NoSQL-databaser, der slet ikke understøtter forespørgsler.

I betragtning af hvor hurtigt vores forståelse af LINQ-stil forespørgselssystemer udvikler sig, ville det helt sikkert være en fejl at tilføje denne funktionalitet direkte til et sprog som Java i øjeblikket. Enhver tilgang kan ende med at blive forældet, og det vil pålægge fremtidige versioner af Java en stor vedligeholdelsesbyrde. Heldigvis kan Java-programmører bruge biblioteker såsom Jinq og jOOQ i stedet, som giver de fleste af fordelene ved LINQ, men som ikke kræver stram sprogintegration som LINQ.

Lightbend opretholder Slick – LINQ til Scala. Hvordan er JINQ sammenlignet med Slick?

De forsøger begge at levere en LINQ-lignende grænseflade til forespørgsler i databaser. Da Slick er designet til Scala, har den en fantastisk Scala-integration og er i stand til at bruge Scalas mere udtryksfulde programmeringsmodel til at give en meget elegant implementering. For at få de fulde fordele ved Slick skal du dog omfavne Scala-økosystemet.

Jinq er primært designet til brug med Java. Den integreres med eksisterende Java-teknologier som JPA og Hibernate. Du behøver ikke at opgive din eksisterende Java-virksomhedskode, når du bruger Jinq, fordi Jinq arbejder med dine eksisterende JPA-entitetsklasser. Jinq er designet til trinvis adoption. Du kan selektivt bruge det nogle steder og falde tilbage til at bruge almindelig JPA-kode andre steder. Selvom Jinq kan bruges med Scala, er det mere nyttigt for organisationer, der bruger Scala, men som ikke har omfavnet hele Scala-økosystemet. For eksempel giver Jinq dig mulighed for at bruge dine eksisterende Hibernate-enheder i din Scala-kode, mens du stadig bruger et moderne funktionelt forespørgselssystem i LINQ-stil til dem.

JINQ har set den største forbedring, da Java 8 introducerede Stream API. Hvad er din mening om funktionel programmering i Java?

Jeg er virkelig glad for, at Java endelig har understøttelse af lambdaer. Det er en kæmpe forbedring, der virkelig gør mit liv som programmør meget nemmere. Med tiden håber jeg dog, at Java-sprogstewarderne vil være i stand til at forfine lambdas yderligere.

Fra Jinqs perspektiv er en af ​​de største svagheder ved Java 8's lambdaer den totale mangel på enhver refleksionsstøtte. Jinq har brug for refleksionsstøtte til at afkode lambdaer og oversætte dem til forespørgsler. Da der ikke er nogen refleksionsstøtte, skal Jinq bruge langsomme og skøre alternative teknikker for at få den samme information. Personligt synes jeg, at manglen på refleksion er en væsentlig forglemmelse, og denne mangel på refleksionsstøtte kan potentielt svække hele Java-økosystemet som helhed på lang sigt.

Jeg har et par små irritationsmomenter med manglende annotationsunderstøttelse og mangel på gode JavaDoc-retningslinjer for, hvordan man behandler lambdaer. Streams API og lambda-metafabrikkerne virker også en anelse overdrevent komplekse for mig, og jeg spekulerer på, om noget enklere ville have været bedre der.

Fra et dagligt programmeringsperspektiv har jeg dog fundet ud af, at manglen på syntaktisk sukker til at kalde lambdas er hovedproblemet, der gentagne gange har frustreret mig. Det virker som en ret mindre ting, men jo mere jeg bruger lambdas, jo mere føler jeg, at det er rigtig vigtigt. I Java 8 er det så nemt at skabe og videregive lambdaer, at jeg normalt helt kan ignorere det faktum, at lambdaer er repræsenteret som klasser med en enkelt metode. Jeg er i stand til at tænke på min kode i form af lambdas. Min mentale model, når jeg skriver Java 8-kode, er, at jeg laver lambdaer og sender dem rundt. Men når jeg faktisk skal påkalde en lambda, bryder lambda-magien fuldstændig sammen. Jeg skal stoppe op og skifte gear og tænke på lambdaer i form af klasser. Personligt kan jeg aldrig huske navnet på den metode, jeg skal kalde for at påberåbe en lambda. Er det run(), accept(), consume() eller anvende()? Jeg ender ofte med at skulle slå op i dokumentationen for metodenavnet, hvilket bryder min koncentration. Hvis Java 8 havde syntaktisk sukker til at kalde lambdas, så ville jeg aldrig behøve at bryde ud af lambda-abstraktionen. Jeg ville være i stand til at skabe, gå rundt og ringe til lambdaer uden at skulle tænke på dem som klasser.

Java 9 introducerer Flow API til reaktiv interoperabilitet. Planlægger du at implementere en reaktiv JINQ?

For at være ærlig er jeg ikke så fortrolig med reaktive API'er. På det seneste har jeg arbejdet mest på desktop-applikationer, så jeg har ikke skullet håndtere problemer i et tilstrækkeligt omfang, hvor en reaktiv tilgang ville give mening.

Du har tidligere nævnt for mig, at du har andre projekter kørende. Hvad arbejder du på lige nu?

Efter et stykke tid er det nemt at akkumulere projekter. Jinq er for det meste stabil i øjeblikket, selvom jeg af og til tilføjer fejlrettelser og andre ændringer. Der er stadig et par vigtige funktioner, der kunne tilføjes, såsom understøttelse af masseopdateringer eller forbedret kodegenerering, men det er ret store tiltag, som ville kræve en del finansiering at udføre.

Jeg arbejder af og til på et programmeringssprog kaldet Babylscript, som er et flersproget programmeringssprog, der lader dig skrive kode på en blanding af fransk, kinesisk, arabisk og andre ikke-engelske sprog. Som et ledsagerprojekt til det driver jeg også en hjemmeside til undervisning i programmering til børn kaldet Programmering Basics, der underviser i programmering på 17 forskellige sprog. I øjeblikket bruger jeg dog det meste af min tid på to projekter. Det ene er et kunstværktøj kaldet Omber, som er et vektortegneprogram, der har specialiseret sig i avancerede gradienter. Det andet projekt involverer at bruge HTML5 som UI-frontend til desktop Java-programmer. Al din UI-kode ville stadig være skrevet i Java, men i stedet for at bruge AWT eller Swing, ville du bare manipulere HTML ved hjælp af en standard DOM-grænseflade bundet til Java. Som en sidegevinst kan al din UI-kode kompileres ved hjælp af GWT til JavaScript, så du også kan genbruge din UI-kode til websider.

Yderligere information

Mange tak for dette meget interessante interview, Ming. Vil du lære mere om JINQ? Læs om det i dette tidligere gæsteindlæg på jOOQ-bloggen, og se Mings JVMLS 2015-tale:


Java tag