Use Built-in Joda Serializer/Deserializer for easy config
If you want custom formatting, there's no need to write a custom Serializer or Deserializer.  jackson-datatype-joda 2.x provides a DateTimeSerializer and DateTimeDeserializer on which you can set a DateTimeFormatter to use, providing you custom formatting. You then add the configured Serializer and/or Deserializer to the JodaModule when you add the module.
An advantage of using the provided Serializer/Deserializer (over custom ones) is that you can annotated any DateTime properties with @JsonFormat(pattern = ". . .") to override the format you configure. The custom Serializers/Deserializers shown in most of the other answers (from the Jackson 1.x days) will not honor @JsonFormat annotations. So if you have that one odd ball DateTime value that needs a a different format, it is easily handled, as shown in the below examples.
In addition to the DateTimeSerializer and DateTimeDeserializer, there are others such as LocalDateSerializer/LocalDateDeserializer, PeriodSerializer/PeriodDeserializer, etc.
Examples
Below is a Java and a Kotlin example. You will need to add the following to your dependencies:
com.fasterxml.jackson.datatype:jackson-datatype-joda:{version}
The examples show setting a default custom format, and overriding that format on a per property basis via the @JsonFormat annotation.
Java Example
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaDateFormat;
import com.fasterxml.jackson.datatype.joda.deser.DateTimeDeserializer;
import com.fasterxml.jackson.datatype.joda.ser.DateTimeSerializer;
import org.joda.time.DateTime;
import org.joda.time.ReadableInstant;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
public class JacksonJodaTimeJavaExample
{
    private static final DateTimeFormatter DATE_TIME_FORMATTER =
        DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZZ");
    private static final JacksonJodaDateFormat JACKSON_JODA_DATE_FORMAT =
        new JacksonJodaDateFormat(DATE_TIME_FORMATTER);
    private static ObjectMapper mapper = createMapper();
    private static ObjectMapper createMapper()
    {
        return JsonMapper
            .builder()
            .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
            .addModule(new JodaModule()
                           // If you do not want to customize the formatting, you can remove the next two lines
                           .addSerializer(new DateTimeSerializer(JACKSON_JODA_DATE_FORMAT))
                           .addDeserializer(ReadableInstant.class, new DateTimeDeserializer(ReadableInstant.class, JACKSON_JODA_DATE_FORMAT))
            )
            // Enable pretty printing for our example
            .enable(SerializationFeature.INDENT_OUTPUT)
            .build();
    }
    record Event(
        DateTime startTime,
        DateTime endTime,
        @JsonFormat(pattern = "yyyy-MM-dd 'at' HH:mm:ss")
        DateTime dateTimeInAlternateFormat
    ) {}
    public static void main(String[] args) throws Exception
    {
        final DateTime now = DateTime.now();
        Event event = new Event(now, now.plusHours(1), now);
        String json = mapper.writeValueAsString(event);
        System.out.println(json);
        Event deserializedEvent = mapper.readValue(json, Event.class);
        System.out.println(deserializedEvent);
    }
}
Kotlin Example
import com.fasterxml.jackson.annotation.JsonFormat
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.datatype.joda.JodaModule
import com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaDateFormat
import com.fasterxml.jackson.datatype.joda.deser.DateTimeDeserializer
import com.fasterxml.jackson.datatype.joda.ser.DateTimeSerializer
import org.joda.time.DateTime
import org.joda.time.ReadableInstant
import org.joda.time.format.DateTimeFormat
object JacksonJodaTimeKotlinExample
{
    private val DATE_TIME_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZZ")
    private val JACKSON_JODA_DATE_FORMAT = JacksonJodaDateFormat(DATE_TIME_FORMATTER)
    private val mapper = createMapper()
    private fun createMapper() =
        JsonMapper
        .builder()
        .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
        .addModule(
            // If you do not want to customize the formatting, you can remove the "apply" block
            JodaModule().apply {
                addSerializer(DateTimeSerializer(JACKSON_JODA_DATE_FORMAT))
                addDeserializer(ReadableInstant::class.java, DateTimeDeserializer(ReadableInstant::class.java, JACKSON_JODA_DATE_FORMAT))
            })
        // Enable pretty printing for our example
        .enable(SerializationFeature.INDENT_OUTPUT)
        .build()
    data class Event(
        var startTime: DateTime? = null,
        var endTime: DateTime? = null,
        // If we want a DateTime in an alternate format, we can
        @JsonFormat(pattern = "yyyy-MM-dd 'at' HH:mm:ss")
        var dateTimeInAlternateFormat: DateTime? = null)
    fun runExample()
    {
        val now = DateTime.now()
        val event = Event(now, now.plusHours(1), now)
        val json = mapper.writeValueAsString(event)
        println(json)
        val deserializedEvent = mapper.readValue(json, Event::class.java)
        println(deserializedEvent)
    }
}
fun main()
{
    JacksonJodaTimeKotlinExample.runExample()
}