Load JavaFX images and save them to the database
JavaFX is a graphical user interface toolkit for the Java programming language that makes it easy to create rich, modern user interfaces. In actual development, sometimes it is necessary to load JavaFX images and save them to the database. This article will introduce you how to do this and help you solve this problem through simple and easy-to-understand steps. Let’s discuss it together!
Question content
I am currently trying to save a small image in the database and load it to be used as a javafx.scene.image.Image. A few solutions I've tried involve using SwingFXUtils.fromFXImage and SwingFXUtils.toFxImage, but that class seems to require an entry in the module-info.java file that I'm not even trying to use.
I encountered this exception when trying to use SwingFXUtils: java.lang.NoClassDefFoundError: javafx/embed/swing/SwingFXUtils
.
When I used the module system, it broke another library I was using (itextpdf). So I want to avoid this and just find another way to save and load JavaFX images from the database. Any suggestions?
Solution
You mentioned:
This is not true. Although javafx only supports loading as named modules 1, javafx does not require that your own code be modular. The only way to get a noclassdeffounderror
is to fail to include the class in the module path/classpath at runtime (it must be present at compile time, otherwise your code will not compile). I recommend reading Getting Started with JavaFX to learn how to set up a basic javafx application using one of the major java IDEs and/or build tools. However, without knowing how you configure your project and run it, we won't be able to tell you what exactly is wrong with your setup.
That is, the overall goal you want to accomplish is indeed possible. Below is an example that allows you to browse for images on your computer and save them to an in-memory h2 database. The id and name of the image are put into a tableview
which contains a column with an "open" button that allows you to view the image loaded from the in-memory database. Images are stored in the database as blobs in png format, regardless of their original format. The swingfxutils
and imageio
classes are used to convert javafx images to png "files" 2.
The example does not show you how to deploy the application (for example, via jpackage
).
Version
Below are the versions of the libraries and tools I used to build and run the examples.
-
java 21.0.1 (eclipse adoptium/temurin)
-
javafx 21.0.1
-
h2 2.2.224
-
gradle 8.4
-
JavaFX Gradle Plugin 0.1.0
In comment to previous question2 you stated that you are using gradle, so what I am using in the example is gradle.
source code
There is no module-info.java
file.
imagerecord.java
package com.example; public record imagerecord(int id, string name) {}
main.java
package com.example; import java.io.file; import java.util.concurrent.executor; import java.util.concurrent.executors; import java.util.function.consumer; import javafx.application.application; import javafx.beans.property.simpleintegerproperty; import javafx.beans.property.simpleobjectproperty; import javafx.beans.property.simplestringproperty; import javafx.concurrent.task; import javafx.geometry.insets; import javafx.geometry.pos; import javafx.scene.scene; import javafx.scene.control.button; import javafx.scene.control.scrollpane; import javafx.scene.control.tablecell; import javafx.scene.control.tablecolumn; import javafx.scene.control.tableview; import javafx.scene.image.image; import javafx.scene.image.imageview; import javafx.scene.layout.borderpane; import javafx.scene.layout.hbox; import javafx.stage.filechooser; import javafx.stage.stage; import javafx.stage.stagestyle; import javafx.stage.window; public class main extends application { private final executor executor = executors.newvirtualthreadpertaskexecutor(); private final imagesdatabase db = new imagesdatabase("test"); private final imagerepository imagerepo = new imagerepository(db); private file lastdirectory; @override public void start(stage primarystage) { var table = createtable(record -> displayimage(primarystage, record)); var choosebtn = new button("choose image..."); choosebtn.setonaction( e -> { e.consume(); var image = chooseimage(primarystage); if (image != null) { executor.execute(createsaveimagetask(image, table.getitems()::add)); } }); var root = new borderpane(); root.settop(choosebtn); root.setcenter(table); borderpane.setalignment(choosebtn, pos.center); borderpane.setmargin(choosebtn, new insets(10)); primarystage.setscene(new scene(root, 600, 400)); primarystage.show(); } @override public void stop() throws exception { db.close(); } private image chooseimage(window owner) { var chooser = new filechooser(); chooser.settitle("choose image file"); chooser .getextensionfilters() .add(new filechooser.extensionfilter("image files", "*.jpeg", "*.jpg", "*.png")); if (lastdirectory != null) { chooser.setinitialdirectory(lastdirectory); } var file = chooser.showopendialog(owner); if (file != null) { lastdirectory = file.getparentfile(); return new image(file.touri().tostring()); } return null; } private void displayimage(window owner, imagerecord record) { var view = new imageview(); var task = creategetimagetask(record, view::setimage); executor.execute(task); var sp = new scrollpane(view); sp.setpannable(true); var window = new stage(stagestyle.utility); window.initowner(owner); window.settitle(record.name()); window.setscene(new scene(sp, 500, 300)); window.setonhiding(e -> task.cancel()); window.show(); } private tableview<imagerecord> createtable(consumer<imagerecord> onopen) { var table = new tableview<imagerecord>(); table.setcolumnresizepolicy(tableview.constrained_resize_policy_flex_last_column); var idcol = new tablecolumn<imagerecord, number>("id"); idcol.setcellvaluefactory(data -> new simpleintegerproperty(data.getvalue().id())); table.getcolumns().add(idcol); var namecol = new tablecolumn<imagerecord, string>("name"); namecol.setcellvaluefactory(data -> new simplestringproperty(data.getvalue().name())); table.getcolumns().add(namecol); var openbtncol = new tablecolumn<imagerecord, imagerecord>(); openbtncol.setcellvaluefactory(data -> new simpleobjectproperty<>(data.getvalue())); openbtncol.setcellfactory(tc -> createopenbuttoncell(onopen)); table.getcolumns().add(openbtncol); return table; } private tablecell<imagerecord, imagerecord> createopenbuttoncell(consumer<imagerecord> onopen) { return new tablecell<>() { final hbox container = new hbox(); final button openbutton = new button("open"); { container.getchildren().add(openbutton); container.setalignment(pos.center); openbutton.setonaction( e -> { e.consume(); var item = isempty() ? null : getitem(); if (item != null) { onopen.accept(item); } }); } @override protected void updateitem(imagerecord item, boolean empty) { super.updateitem(item, empty); if (empty || item == null) { setgraphic(null); } else { setgraphic(container); } } }; } private task<?> createsaveimagetask(image image, consumer<imagerecord> onsuccess) { return new task<imagerecord>() { @override protected imagerecord call() throws exception { return imagerepo.insertimage(image); } @override protected void succeeded() { onsuccess.accept(getvalue()); } @override protected void failed() { getexception().printstacktrace(); } }; } private task<?> creategetimagetask(imagerecord record, consumer<image> onsuccess) { return new task<image>() { @override protected image call() throws exception { return imagerepo.getimage(record).orelsethrow(); } @override protected void succeeded() { onsuccess.accept(getvalue()); } @override protected void failed() { getexception().printstacktrace(); } }; } }
imagerepository.java
package com.example; import static java.sql.statement.return_generated_keys; import java.io.bytearrayinputstream; import java.io.bytearrayoutputstream; import java.io.ioexception; import java.io.inputstream; import java.io.uncheckedioexception; import java.sql.sqlexception; import java.util.arraylist; import java.util.list; import java.util.objects; import java.util.optional; import java.util.concurrent.atomic.atomicinteger; import javafx.embed.swing.swingfxutils; import javafx.scene.image.image; import javax.imageio.imageio; public class imagerepository { private static final string select_all_records_sql = "select id, name from images"; private static final string select_image_sql = "select image from images where id = ?"; private static final string insert_sql = "insert into images (name, image) values (?, ?)"; private final atomicinteger generatednamecount = new atomicinteger(); private final imagesdatabase db; public imagerepository(imagesdatabase db) { this.db = db; } public list<imagerecord> getrecords() throws sqlexception { return db.execute( conn -> { try (var stat = conn.createstatement()) { var result = stat.executequery(select_all_records_sql); var records = new arraylist<imagerecord>(); while (result.next()) { int id = result.getint(1); var name = result.getstring(2); records.add(new imagerecord(id, name)); } return records; } }); } public optional<image> getimage(imagerecord record) throws sqlexception { return getimage(record.id()); } public optional<image> getimage(int recordid) throws sqlexception { if (recordid <= 0) { throw new illegalargumentexception("recordid <= 0: " + recordid); } return db.execute( conn -> { try (var stat = conn.preparestatement(select_image_sql)) { stat.setint(1, recordid); var result = stat.executequery(); if (result.next()) { var image = new image(result.getbinarystream(1)); return optional.of(image); } else { return optional.empty(); } } }); } public imagerecord insertimage(image image) throws sqlexception { objects.requirenonnull(image); return db.execute( conn -> { try (var stat = conn.preparestatement(insert_sql, return_generated_keys)) { var name = getimagename(image); stat.setstring(1, name); stat.setbinarystream(2, imagetoinputstream(image)); stat.executeupdate(); var keys = stat.getgeneratedkeys(); if (keys.next()) { int id = keys.getint(1); return new imagerecord(id, name); } else { throw new illegalstateexception("generated key not returned"); } } }); } private string getimagename(image image) { var source = image.geturl(); return source == null ? generateimagename() : source; } private string generateimagename() { return "generated image name " + generatednamecount.incrementandget(); } private inputstream imagetoinputstream(image image) { var out = new bytearrayoutputstream(); try { imageio.write(swingfxutils.fromfximage(image, null), "png", out); } catch (ioexception ex) { throw new uncheckedioexception(ex); } return new bytearrayinputstream(out.tobytearray()); } }
imagesdatabase.java
package com.example; import java.sql.connection; import java.sql.sqlexception; import java.util.objects; import java.util.concurrent.locks.lock; import java.util.concurrent.locks.reentrantlock; import javax.sql.datasource; import org.h2.jdbcx.jdbcdatasource; public class imagesdatabase implements autocloseable { private static final string create_table_sql = "create table images (id identity, name varchar(255), image blob)"; @functionalinterface public interface sqlfunction<t> { t execute(connection connection) throws sqlexception; } private final lock mutex = new reentrantlock(); private final datasource source; private connection connection; private boolean open = true; private boolean initialized; public imagesdatabase(string name) { if (name.isblank()) { throw new illegalargumentexception("blank name"); } var source = new jdbcdatasource(); source.seturl("jdbc:h2:mem:" + name + ";db_close_delay=-1"); this.source = source; } public <t> t execute(sqlfunction<t> function) throws sqlexception { objects.requirenonnull(function); mutex.lock(); try { checkopen(); return function.execute(getoropenconnection()); } finally { mutex.unlock(); } } private connection getoropenconnection() throws sqlexception { if (connection == null || connection.isclosed()) { connection = source.getconnection(); initialize(connection); } return connection; } private void initialize(connection conn) throws sqlexception { if (!initialized) { try (var stat = conn.createstatement()) { stat.executeupdate(create_table_sql); } initialized = true; } } private void shutdown() throws sqlexception { if (initialized) { try (var conn = getoropenconnection(); var stat = conn.createstatement()) { stat.execute("shutdown"); } connection = null; } } private void checkopen() { if (!open) { throw new illegalstateexception("closed"); } } @override public void close() throws sqlexception { mutex.lock(); try { if (open) { open = false; shutdown(); } } finally { mutex.unlock(); } } }
gradle file
I used kotlin dsl, but you can also use groovy dsl if you prefer.
settings.gradle.kts
rootproject.name = "h2images-example"
build.gradle.kts
plugins { id("org.openjfx.javafxplugin") version "0.1.0" application } group = "com.example" version = "1.0" javafx { modules("javafx.controls", "javafx.swing") version = "21.0.1" } application { mainclass.set("com.example.main") } repositories { mavencentral() } dependencies { implementation("com.h2database:h2:2.2.224") }
implement
You can use the following command to execute the above:
./gradlew run
Note ./gradlew
calls Gradle Wrapper. If you have a version of gradle installed on your machine, you can generate a wrapper for version 8.4 via:
gradle wrapper --gradle-version 8.4
1. javafx does not technically support loading from the classpath. This means that ideally, the javafx module should be on the module path and resolve to a named module, even if your own code and other dependencies are loaded from the classpath. However, I don't know what Breaking will happen if javafx is on the classpath (at least as of javafx 21), except that your main class can no longer be a subclass of javafx.application. application
(you need a separate "launcher class" as the main class). Just know that any issues caused by javafx being on the classpath are unlikely to be fixed by the javafx team.
Please note that the gradle and maven plugins provided by openjfx configure these build tools to place javafx on the module path.
2. Based on the context of both of your questions, this example converts an image
object to png bytes. However, if you already receive the image as bytes (i.e. as a local or remote file), it may be easier and more efficient to put those bytes directly into the database.
The above is the detailed content of Load JavaFX images and save them to the database. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics



Bitcoin, as a cryptocurrency, has experienced significant market volatility since its inception. This article will provide an overview of the historical price of Bitcoin since its birth to help readers understand its price trends and key moments. By analyzing Bitcoin's historical price data, we can understand the market's assessment of its value, factors affecting its fluctuations, and provide a basis for future investment decisions.

Since its creation in 2009, Bitcoin’s price has experienced several major fluctuations, rising to $69,044.77 in November 2021 and falling to $3,191.22 in December 2018. As of December 2024, the latest price has exceeded $100,204.

Real-time Bitcoin USD Price Factors that affect Bitcoin price Indicators for predicting future Bitcoin prices Here are some key information about the price of Bitcoin in 2018-2024:

Yes, H5 page production is an important implementation method for front-end development, involving core technologies such as HTML, CSS and JavaScript. Developers build dynamic and powerful H5 pages by cleverly combining these technologies, such as using the <canvas> tag to draw graphics or using JavaScript to control interaction behavior.

The method of customizing resize symbols in CSS is unified with background colors. In daily development, we often encounter situations where we need to customize user interface details, such as adjusting...

Regarding the reasons and solutions for misaligned display of inline-block elements. When writing web page layout, we often encounter some seemingly strange display problems. Compare...

How to use JavaScript or CSS to control the top and end of the page in the browser's printing settings. In the browser's printing settings, there is an option to control whether the display is...

The problem of container opening due to excessive omission of text under Flex layout and solutions are used...